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