Thread overview
Getting the address of a member function at compile time inside a member function
Jul 06
user1234
Jul 06
user1234
6 days ago
Johan
4 days ago
Paul Backus
5 days ago
Walter Bright
4 days ago
user1234
July 06

In discord, a user had the following puzzle:

struct S
{
   void foo() {}
   void bar() {
       static void function() fn = &foo;
   }
}

This does not compile. The compiler says it cannot resolve the this pointer at compile time.

Fine, so let's try naming the type!

static void function() fn = &S.foo;

Nope, S is just a namespace at this point.

What about just asking for the funcptr?

static void funciont() fn = &foo.funcptr;

Nope, still the same error. I tried some other stuff, and it also told me it cannot evaluate funcptr at compile time.

I found the following workaround works, but it's not very nice that you have to do this:

enum fnptr(alias fn) = &fn;

static void function() fn = fnptr!foo;

Now, it just gets the function pointer.

Does anyone have a solution to this that does not involve a workaround that requires writing a helper thing? It appears there is no way to tell the compiler you don't want the context pointer.

-Steve

July 06

On Sunday, 6 July 2025 at 01:10:16 UTC, Steven Schveighoffer wrote:

>

What about just asking for the funcptr?

static void funciont() fn = &foo.funcptr;

Nope, still the same error. I tried some other stuff, and it also told me it cannot evaluate funcptr at compile time.

You'll hate my answer but actually that was almost that, you forgot the parens

auto fn = (&foo).funcptr;
July 06

On Sunday, 6 July 2025 at 01:25:18 UTC, user1234 wrote:

>

On Sunday, 6 July 2025 at 01:10:16 UTC, Steven Schveighoffer wrote:

>

What about just asking for the funcptr?

static void funciont() fn = &foo.funcptr;

Nope, still the same error. I tried some other stuff, and it also told me it cannot evaluate funcptr at compile time.

You'll hate my answer but actually that was almost that, you forgot the parens

auto fn = (&foo).funcptr;

Sorry it's run-time... same error with static as storage class.

July 05
When taking the address of a member function, a delegate is created, not a function pointer. The delegate is comprised of two members: a function pointer, and a context pointer (i.e. `this`). When the static fn is initialized, there is no `this` available at compile time, so it (correctly) fails to compile.

The workaround you figured out is pretty clever! I don't think you'll be able to do better.
July 07

On Sunday, 6 July 2025 at 03:47:07 UTC, Walter Bright wrote:

>

When taking the address of a member function, a delegate is created, not a function pointer. The delegate is comprised of two members: a function pointer, and a context pointer (i.e. this). When the static fn is initialized, there is no this available at compile time, so it (correctly) fails to compile.

That is not what happens when outside a member function. I should have been more descriptive.

void function() fn = &S.bar; // ok!
struct S {
   void bar() {}
   static void function() fn = &bar; // ok!
   void foo() {
       static x() => &bar;
       static void function() fn = x(); // ok!

       enum fnptr(alias fn) = &fn;
       static void function() fn2 = fnptr!bar; // ok!

       // these all error
       // static void function() fn3 = &bar;
       // static void function() fn4 = &S.bar;
       // static void function() fn5 = (&bar).funcptr;
   }
}

It seems the act of being in a member function engages the compiler to try and add a context pointer. And there's no way to turn this off, except to not be in a member function.

>

The workaround you figured out is pretty clever! I don't think you'll be able to do better.

Probably. But it does seem odd to have to jump through this hoop.

-Steve

6 days ago

On Monday, 7 July 2025 at 03:28:58 UTC, Steven Schveighoffer wrote:

>

On Sunday, 6 July 2025 at 03:47:07 UTC, Walter Bright wrote:

>

When taking the address of a member function, a delegate is created, not a function pointer. The delegate is comprised of two members: a function pointer, and a context pointer (i.e. this). When the static fn is initialized, there is no this available at compile time, so it (correctly) fails to compile.

That is not what happens when outside a member function. I should have been more descriptive.

void function() fn = &S.bar; // ok!
struct S {
   void bar() {}

Very surprising that this works, looks like a clear bug to me because it's apparently also allowed for bar to access member variables. It results in nullptr dereference upon using this inside bar.

Failure example:

import std.stdio;

void function() fn = &S.bar;
struct S {
  int x;
  void bar() { writeln(x); }
}

void main() {
    fn();
}

https://d.godbolt.org/z/E5jWb9Wd3

-Johan

5 days ago
On 7/6/2025 8:28 PM, Steven Schveighoffer wrote:
> It seems the act of being in a member function engages the compiler to try and add a context pointer. And there's no way to turn this off, except to not be in a member function.

But non-static member functions must have a context pointer! It's the whole point.

4 days ago

On Friday, 11 July 2025 at 10:42:46 UTC, Johan wrote:

>

On Monday, 7 July 2025 at 03:28:58 UTC, Steven Schveighoffer wrote:

>
void function() fn = &S.bar; // ok!
struct S {
   void bar() {}

Very surprising that this works, looks like a clear bug to me because it's apparently also allowed for bar to access member variables. It results in nullptr dereference upon using this inside bar.

Known bug, reported in 2010:

https://issues.dlang.org/show_bug.cgi?id=3720

4 days ago

On Sunday, 13 July 2025 at 02:53:24 UTC, Walter Bright wrote:

>

On 7/6/2025 8:28 PM, Steven Schveighoffer wrote:

>

It seems the act of being in a member function engages the compiler to try and add a context pointer. And there's no way to turn this off, except to not be in a member function.

But non-static member functions must have a context pointer! It's the whole point.

I would suggest to make the compiler recognize the pattern (&SomeStruct.memberFun).funcptr during AddrExp sema and allow that in a static context.

4 days ago

On Sunday, 13 July 2025 at 02:53:24 UTC, Walter Bright wrote:

>

On 7/6/2025 8:28 PM, Steven Schveighoffer wrote:

>

It seems the act of being in a member function engages the compiler to try and add a context pointer. And there's no way to turn this off, except to not be in a member function.

But non-static member functions must have a context pointer! It's the whole point.

In general, this function pointer is still a valid function pointer, and there is value to accessing the pointer. e.g. assert(dg.funcptr == &S.foo);

Note, you can use function pointers to build delegates:

import std.stdio;
import std.random;

struct S
{
   void foo() { writeln("foo"); }
   void bar() { writeln("bar"); }
}

void main()
{
   void function()[] fns = [&S.foo, &S.bar];
   void delegate() dg;
   S s;
   dg.ptr = &s;
   dg.funcptr = fns[uniform(0, 2)];
   dg();
}

I would be excited if D could improve on this. Perhaps by allowing a type like void function(ref S this), which actually could be called with an S. It could also make delegate types more useful and safer.

-Steve