June 11, 2013 Re: Member function pointers | ||||
---|---|---|---|---|
| ||||
Attachments:
| Manu, is this that you want? class C { int foo() { printf("invoke C.foo()\n"); return 1; } int bar() { printf("invoke C.bar()\n"); return 2; } int bar(string s) { printf("invoke C.bar(string)\n"); return s.length; } } class D : C { override int foo() { printf("invoke D.func\n"); return 4; } } void main() { D d = new D(); MemFunPtr!C.foo!() fpFoo = d; MemFunPtr!C.bar!() fpBar = d; MemFunPtr!C.bar!(string) fpBar2 = d; assert(fpFoo() == 4); assert(fpBar() == 2); assert(fpBar2("hello") == 5); static assert(fpFoo.sizeof == (void*).sizeof); } // --------------------- // Implementation // --------------------- import std.traits, std.typetuple; template MemFunPtr(C) if (is(C == class)) { mixin GenName!C; } mixin template GenName(C, size_t i = 0) { alias names = allMembers!C; static if (i >= names.length) { /* do nothing */ } else { enum name = names[i]; alias vfuncs = TypeTuple!(__traits(getVirtualFunctions, C, name)); static if (vfuncs.length > 0) { struct _Impl(Params...) { private: C obj; alias VtblEntry = GetVtblEntry!(C, name, Params); private: this(C obj) { this.obj = obj; } auto ref opCall(Params args) { alias R = ReturnType!(VtblEntry.func); R delegate(Params) dg; dg.ptr = *cast(void**)&obj; dg.funcptr = cast(R function(Params))obj.__vptr[VtblEntry.vindex]; return dg(args); } } mixin("alias _Impl "~name~";"); } mixin .GenName!(C, i + 1); } } template GetVtblEntry(C, string name, Params...) { alias names = allMembers!C; template Impl(size_t ofs, size_t i) { alias vfuncs = TypeTuple!(__traits(getVirtualFunctions, C, names[i])); static if (names[i] != name) { alias Impl = Impl!(ofs + vfuncs.length, i + 1); } else { static assert(vfuncs.length > 0); template Impl2(size_t j) { static if (is(ParameterTypeTuple!(vfuncs[j]) == Params)) { enum vindex = ofs + j; alias func = vfuncs[j]; } else alias Impl2 = Impl2!(j + 1); } alias Impl = Impl2!(0); } } alias GetVtblEntry = Impl!(1/*vtbl[0] == TypeInfo*/, 0); } template baseMembers(BC...) { static if (BC.length > 1) { alias baseMembers = TypeTuple!( allMembers!(BC[0]), baseMembers!(BC[1 .. $]) ); } else static if (BC.length == 1) { alias baseMembers = allMembers!(BC[0]); } else alias baseMembers = TypeTuple!(); } template derivedMembers(C) { alias derivedMembers = TypeTuple!( __traits(derivedMembers, C) ); } template allMembers(C) { alias allMembers = TypeTuple!( baseMembers!(BaseClassesTuple!C), __traits(derivedMembers, C) ); } Kenji Hara 2013/6/8 Manu <turkeyman@gmail.com> > So from my dconf talk, I detailed a nasty hack to handle member function > pointers in D. > My approach is not portable, so I'd like to see an expression formalised > in D, so this sort of interaction with C++ is possible, and also it may be > useful in D code directly. > > I'm thinking something like this... Keen to hear thoughts. > > My approach was this: > void function(T _this, ...args...); > > Explicit 'this' pointer; only works with ABI's that pass 'this' as the first integer argument. > > What I suggest is: > void function(T this, ...args...); > > Note, I use keyword 'this' as the first argument. This is the key that distinguishes the expression as a member-function pointer rather than a typical function pointer. Calls through this function pointer would know to use the method calling convention rather than the static function calling convention. > > For 'extern(C++) void function(T this)', that would be to use the C++ > 'thiscall' convention. > > I think this makes good sense, because other than the choice of calling convention, it really is just a 'function' in every other way. > > Now taken this as a declaration syntax, I think calls would be made via UFCS. > > T x; > void function(T this) mp; > > mp(x); // I guess this is fine > x.mp(); // but UFCS really makes this concept nice! > > So the final detail, is how to capture one of these member function > pointers from within D... > I initially thought about a syntax, but this is so niche, I don't think it > warrants a syntax. > So my current best idea is to introduce 2 properties to delegates, so that > the function pointer (of this type) can be accessed via the delegate syntax. > > delegate d = &x.f; > void function(T this) mp = d.funcPtr; > > An interesting side effect, is that 'delegate' could actually be understood as a strongly-typed small struct, whereas currently, it's just a magic thing: > > Given: RT delegate(A x, B y) d = &c.m; > > It would look like: > struct delegate(C) > { > C thisPointer; > RT function(C this, A x, B, y) funcPointer; > } > > Currently, to get the instance or function pointers from a delegate, you > need to do something like: > delegate d; > void** pd = cast(void**)&d; > T instancePointer = cast(T)pd[0]; > void function(T this) functionPointer = cast(RT function(T this))pd[1]; > > Casting through a void array like that is pretty horrible. > Adding 2 properties to delegate to get either of those things can makes > sense once it is possible to express the type of the function pointer, > which would now be possible with the syntax above. > > So, I quite like the transparency introduced when a delegate can be explicitly described in the language. But there is one loose detail... > > void f() > { > void g() {} > void delegate() d = &g; // delegate 'this' is a closure > } > > I don't know a syntax to describe the type of a closure, so when a delegate is typed with a closure instead of a struct/class, what is 'C' in the struct template above? > > > Thoughts? > Is there reason to outright ban this sort of expression? > I think this actually clarifies some details of the language, and reduces > a currently 'magic' thing into a well-defined, strongly-typed concept. > > - Manu > |
Copyright © 1999-2021 by the D Language Foundation