Skip to content

Instantly share code, notes, and snippets.

@schancel
Last active August 29, 2015 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save schancel/3ad09f6f0223935301b1 to your computer and use it in GitHub Desktop.
Save schancel/3ad09f6f0223935301b1 to your computer and use it in GitHub Desktop.
Concepts checker for D.
module concepts;
private import std.typetuple;
private import std.typecons;
private import std.traits;
private import std.stdio;
private template getMemberType(C, string memberName)
{
alias member = TypeTuple!(__traits(getMember, C, memberName))[0];
static if ( __traits(compiles, typeof(&member) ) ) {
static if( isSomeFunction!(typeof(&member)) ) {
alias getMemberType = typeof(&member);
} else {
alias getMemberType = typeof(member);
}
} else {
alias getMemberType = typeof(member);
}
}
private template getPropertyType(C, string memberName)
{
alias member = TypeTuple!(__traits(getMember, C, memberName))[0];
alias getPropertyType = typeof(member);
}
/**
Returns $(D true) if $(D T) supports the concept $(D C). Concepts should be defined as in the following example:
----
class CInputRange(E)
{
abstract void popFront();
@property abstract E front();
bool empty;
//Optional Axioms function. This will be checked if it compiles, and that it returns true if it does.
static bool Axioms(T)()
{
return true;
}
}
----
*/
bool isConcept(T,C)()
{
static if( is(typeof(C) == object) )
return true;
foreach( member; __traits(derivedMembers, C) )
{
static if(member != "this")
{
alias ConceptMemberType = getMemberType!(C, member);
static if( member == "Axioms" ) {//Axiom function is not intended to be in checked
static if( ! is( typeof( C.Axioms!T() ) ) || ! C.Axioms!T() ) {
//pragma(msg, T.stringof ~ " fails axioms of " ~ C.stringof);
return false;
}
} else static if( __traits(compiles, __traits(getMember, T, member)) ) {
alias CheckType = getMemberType!(T, member);
static if(isSomeFunction!(ConceptMemberType)) {
static if( ! is(CheckType : ConceptMemberType)
|| !is( getPropertyType!(C, member) : getPropertyType!(T, member))) {
//pragma(msg, T.stringof ~ "." ~ member ~ " is not the same type as " ~ C.stringof ~ "." ~ member)
return false;
}
} else {
static if( ! is( getPropertyType!(C, member) : getPropertyType!(T, member) ) ) {
//pragma(msg, T.stringof ~ "." ~ member ~ " is not the same type as " ~ C.stringof ~ "." ~ member)
return false;
}
}
} else {
//pragma(msg, T.stringof ~ " lacks member " ~ member);
return false;
}
}
}
alias parent = TypeTuple!(__traits(parent, C))[0];
static if( is( parent == C) ) {
return true;
} else {
return isConcept!(T, __traits(parent, C))();
}
}
class CInputRange(E)
{
abstract void popFront();
@property abstract E front();
bool empty;
}
class CInfiniteInputRange(E) : CInputRange!E
{
static bool Axioms(T)() {
enum empty = T.empty;
return !empty;
}
}
struct StringRange
{
string[] items;
@property string front() { return "Element";}
void popFront() {}
void extraFunction() {}
@property bool empty() { return items.length == 0;}
}
unittest
{
static assert( !isConcept!(StringRange, CInfiniteInputRange!string), "Supports InfiniteInputRange?" );
static assert( isConcept!(StringRange, CInputRange!string), "Does not support CInputRange?" );
}
int main()
{
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment