static vs. dynamic interfaces

Philippe Sigaud philippe.sigaud at gmail.com
Tue Jan 3 12:33:17 PST 2012


Hi Trass3r:
On Tue, Jan 3, 2012 at 13:52, Trass3r <un at known.com> wrote:
> bump

It's possible alright. The only difficulty is in getting all overloads
of a given member resolved. This is what the AllMembers template gets
(see below). It works on all aggregates (structs, classes, modules),
for fields, aliases and methods.

import std.typetuple;

/**
 * Stores a member description as a name-Type pair
 */
struct Member(string n, T)
{
    enum name = n; // for external access
    alias T Type;
}

/**
 * Helper template
 */
template MakeMember(alias member)
{
    alias Member!(__traits(identifier, member), typeof(member)) MakeMember;
}

/**
 * Helper template to make is(__traits()) compile. Bug?
 */
template Alias(alias a)
{
    alias a Alias;
}

/**
 * Gets the overloads of a given member, as a Member type tuple.
 */
template Overloads(alias a, string member)
{
    static if (__traits(getOverloads, a, member).length > 0) // Method
        alias staticMap!(MakeMember, __traits(getOverloads,a, member))
Overloads;
    else // field or alias
        static if (is(typeof(__traits(getMember, a, member)))) //
Field or symbol alias
            alias TypeTuple!(Member!(member,
typeof(__traits(getMember, a, member)))) Overloads;
        else static if (is(Alias!(__traits(getMember, a, member)))) //
Type alias
            alias TypeTuple!(Member!(member, __traits(getMember, a,
member))) Overloads;
        else // template?
            alias TypeTuple!(Member!(member, void)) Overloads;
}

/**
 * Helper template, to be mapped in AllMembers.
 */
template GetOverloads(alias a)
{
    template GetOverloads(string member)
    {
        alias Overloads!(a, member) GetOverloads;
    }
}

/**
 * Gives a list of all members, distinguishing between different overloads.
 * Gets the fields as well as the methods.
 */
template AllMembers(alias a)
{
    alias staticMap!(GetOverloads!(a), __traits(allMembers, a)) AllMembers;
}

/**
 * Statically check if symbol a implements interface I
 * (that is, if all members of I are found in members of a)
 */
template implements(alias a, I) if (is(I == interface))
{
    alias implementsImpl!(a, AllMembers!I) implements;
}

template implementsImpl(alias a, Items...)
{
    static if (Items.length == 0)
        enum implementsImpl = true;
    else static if (staticIndexOf!(Items[0], AllMembers!a) == -1)
        enum implementsImpl = false;
    else
        enum implementsImpl = implementsImpl!(a, Items[1..$]);
}

interface I
{
    int foo(int i);
    void foo();

    string toString();
}

class Bad
{
    void foo(int i) {}
}

/**
 * Show all examples I could think of
 */
struct Good
{
    int field;

    int foo(int i) { return i;}
    void foo() { writeln("Hello, World!");}

    string toString() { return "I'm a good struct!";}

    alias double Type;
    alias field baz;

    template Id(T) { alias T Id;}
}

void main(string[] args)
{
    assert(implements!(Good, I));
    assert(!implements!(Bad, I));

    foreach(elem; AllMembers!Good) writeln(elem.name, ", of type:",
elem.Type.stringof);
}

I decided to give templates a void type. That's a bit arbitrary.
Also, internal unit tests are not managed correctly, though they are
members and should appear in an AllMembers list.

But I find this a bit limitating. With interfaces, you can imitate
isInputRange, but not isForwardRange (no way to test for assignments),
nor isRandomAccessRange, no field access...
The way it's done in Phobos seems much more powerful to me.


Philippe


More information about the Digitalmars-d mailing list