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