digitalmars.com                        
Last update Sat Oct 7 16:49:29 2023

Member Function Pointers in D

written by Walter Bright

C++ and D share in common the notion of being able to take the address of a function, pass it around, and call that function. It's called a function pointer, which we all know and love. That works fine for regular functions, but what about member functions of a class? Calling a member function needs a so-called ‘this’ pointer — a reference to the object upon which the member function acts.

C++ and D diverge here. D has the notion of a delegate, aka “fat pointer”, which is a pair consisting of a pointer to the member function and a pointer to the ‘this’ object. The virtualness of the member function was resolved at the point where the address of the member function was taken,

class C {
  int a;
  int foo(int i) { return i + a; }
}

auto c = new C();
auto dg = &c.foo;
dg(1);

because the function and its object are bound together at that point, rather than at the call point.

Alternatively, C++ has the notion of a member function pointer, which is independent of whatever object is used to call it. Such an object is provided when the member function is called:

class C {
 public:
  int a;
  virtual int foo(int i) { return i + a; }
};

auto mfp = &C::foo;
auto c = new C();
(c->*mfp)(1);

If C::foo() is a virtual function, then which function to call is not resolved at mfp assignment time, but at mfp call time when the actual object is supplied.

(See Bartosz Milewski's C++ In Action for a nice use case for C++ member function pointers.)

Can this pattern be duplicated in D?

Fortunately, there is an easy way [1]:

class C {
  int a;
  int foo(int i) { return i + a; }
}

auto mfp = function(C self, int i) { return self.foo(i); };
auto c = new C();
mfp(c, 1);

It is line for line analogous to the C++ one. Pretty much the only interesting bit is the use of an anonymous lambda function to lazily bind the object with the function and resolve any virtualness. Those familiar with the guts of how C++ compilers implement member function pointers [2] will recognize this lambda function as pretty much what happens under the C++ hood.

Conclusion

Lambda functions are incredibly powerful, and for those not used to them they continue to surprise and delight in what they can do in a simple, straightforward manner.

References

  1. Timon Gehr's example
  2. Member Function Pointers and the Fastest Possible C++ Delegates by Don Clugston

Acknowledgements

Thanks to Jason House and Bartosz Milewski for their helpful comments on this.

Home | Runtime Library | IDDE Reference | STL | Search | Download | Forums