Last active
August 29, 2015 14:07
-
-
Save schancel/3ad09f6f0223935301b1 to your computer and use it in GitHub Desktop.
Concepts checker for D.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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