Search code examples
templatesd

Check for symbol is a [pointer to] member function/field in D


In C++ we can easily detect that given type is a pointer to member function/field:

template< class T >
struct is_member_pointer : std::false_type {};

template< class T, class U >
struct is_member_pointer<T U::*> : std::true_type {};

There are no such syntax in D (I mean T U::*). Moreover, free functions and methods have the same type:

void func();

struct S
{
    int x;
    void func() {}
}

static assert(is(typeof(func) == typeof(S.func)));

So, my question is: can we write in D a template which is analogous to C++-version?

template is_member_of(alias M)
{
    alias T = /**/;
}

static assert(is(is_member_of!(S.func) == S));
static assert(is(is_member_of!func == void));
static assert(is(is_member_of!(S.x) == S));

Solution

  • Try taking the address of the function via the init object instead:

    static assert(is(typeof(&func) == typeof(&S.init.func)));
    

    Will give:

    ooo.d(9): Error: static assert:  is(void function() == void delegate()) is false
    

    The member function (unless it is static, but then it isn't really a member function) will be typed delegate because it requires a this object, whereas the other one will be typed function.

    That will work for the function, and you can do something similar with the variable (tip: static assert: is(typeof(& x)) is false, but static assert(is(typeof(&S.init.x)); passes - note the .init) if you are curious about if it is an actual member, with a runtime this requirement (that is, not static).

    I'll leave turning this information into a template check as an exercise to the reader (tip though: is(typeof(something) == delegate) is a thing in the language...)

    But, if you want to just know if there's a parent type on the symbol, there's a different way: just ask the compiler if there's a parent! OK, I confess the code is slightly longer to get the void response your static asserts were looking for, but not much:

    // identity template to hack around parser limitations
    // this is common when using the __traits stuff, alas.
    alias I(alias T) = T;
    
    template is_member_of(alias M) {
            // if it has a parent, return it, otherwise, return void
            static if(is(I!(__traits(parent, M))))
                    alias is_member_of = I!(__traits(parent, M));
            else
                    alias is_member_of = void;
    }
    

    You might furthermore check if the parent is certain things like struct or class, but really, if it exists at all it is probably what you're looking for.