Member function pointers
Kenji Hara
k.hara.pg at gmail.com
Mon Jun 10 21:09:02 PDT 2013
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 at 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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.puremagic.com/pipermail/digitalmars-d/attachments/20130611/54718f09/attachment-0001.html>
More information about the Digitalmars-d
mailing list