www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - static vs. dynamic interfaces

reply "Trass3r" <un known.com> writes:
phobos currently checks "static interfaces" with something like 
https://github.com/D-Programming-Language/phobos/blob/master/std/range.d#L213

I wonder if stuff like that could be unified by only defining 
normal interfaces and "automating" the static checking with 
something like

template implements(T, I) if (is(I == interface))
{
    enum implements = is(typeof({foreach(t;__traits(allMembers,I)) 
// check T}));
}

Possible? Reasonable?
Dec 28 2011
parent reply "Trass3r" <un known.com> writes:
bump
Jan 03 2012
parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
Hi Trass3r:
On Tue, Jan 3, 2012 at 13:52, Trass3r <un 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
Jan 03 2012