Would this be a useful addition for Phobos?

They're type-checked member function/variable pointers for D.

I haven't tested them with opDispatch or 'alias this' yet, but they should work correctly for regular members.


The usage is pretty simple... it even works at compile time:

struct Foo { int x; }
const a = MemberPtr!(Foo, q{x});   // shorthand
pragma(msg, a.get(Foo(1)));  // 1

MemberPtr!(Foo, int) b = MemberPtr!(Foo, q{x});  // longhand
auto f = Foo(2);
b.deref(f) = 3;   // by reference, unlike 'get'
assert(f.x == 3);

and it should also work for member functions.



//----------------------------------------

template MemberPtr(T, string memberName) if (isInstanceMember!(T, memberName))
{
        enum MemberPtr =
                MemberPtr!(T, typeof(__traits(getMember, T, memberName)))(
                        staticIndexOf!(memberName, __traits(allMembers, T)));
}

struct MemberPtr(T, TMember)
{
        size_t index = -1;

        auto address(ref inout(T) o) inout
        {
                foreach (i, m; __traits(allMembers, T))
                {
                        static if (isInstanceMember!(T, m, TMember))
                        {
                                // 'switch' might be faster
                                // but the optimizer can worry about that 
instead :)
                                if (this.index == i)
                                {
                                        return &__traits(getMember, o, m);
                                }
                        }
                }
static if (is(typeof(return)) /*has the return type been inferred yet?*/)
                {
                        assert(0, "Invalid member pointer!");
                }
                else { return (inout(TMember)*).init; }
        }

        ref auto deref(ref inout(T) o) inout
        {
                static if (isSomeFunction!(ReturnType!(typeof(&this.address))))
                {
                        return this.address(o);
                }
                else
                {
                        return *this.address(o);
                }
        }

        ref auto get(inout(T) o) inout
        {
                return this.deref(o);
        }
}

private template isStaticFunction(T, string m)
{
        static if (
                __traits(compiles,
                        __traits(isStaticFunction, __traits(getMember, T, m))))
        {
                 enum isStaticFunction =
                         __traits(isStaticFunction, __traits(getMember, T, m));
        }
        else { enum isStaticFunction = false; }
}

private template isInstanceMember(T, string m, TMember = void)
        if (hasMember!(T, m))
{
        enum isInstanceMember =
                !isStaticFunction!(T, m) &&
                (__traits(compiles, __traits(getMember, T, m).offsetof) ||
                        isSomeFunction!(__traits(getMember, T, m))) &&
                (is(TMember == void) ||  /* optional */
                        is(typeof(__traits(getMember, T.init, m)) : TMember));

}
unittest
{
        struct S
        {
                int m1;
                void m2() { }
                static __gshared int s1;
                static void s2() { }
        }
        static assert(isInstanceMember!(S, q{m1}));
        static assert(isInstanceMember!(S, q{m2}));
        static assert(!isInstanceMember!(S, q{s1}));
        static assert(!isInstanceMember!(S, q{s2}));
}

Reply via email to