Created
October 18, 2014 22:45
-
-
Save schancel/dc1f9f105dc2c3bc182a to your computer and use it in GitHub Desktop.
Concept diagnostic diagnostic messages
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.traits; | |
//Gets the member type, even for properties. | |
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). Note, templated member functions are not supported currently. | |
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, bool printMsgs = false)() | |
{ | |
//Make sure to go through base classes, which can also be required. | |
static if( is(C == class)) | |
alias cTypes = TypeTuple!(C, BaseClassesTuple!(C)); | |
else | |
alias cTypes = TypeTuple!(C); | |
foreach(cType; cTypes) static if( ! is(cType == Object) ) | |
foreach( member; __traits(derivedMembers, cType) ) | |
{ | |
//pragma(msg, __traits(getMember, cType, member )); | |
static if(member != "this") { | |
alias MemberSymbol = TypeTuple!(__traits(getMember, cType, member))[0]; | |
static if( is( MemberSymbol A : Concept ) ) | |
{ | |
static if( ! isConcept!(T, A, printMsgs) ) | |
return false; | |
} else { | |
alias ConceptMemberType = getMemberType!(cType, member); | |
static if( member == "Axioms" ) {//Axiom function is not intended to be in checked | |
static if( ! is( typeof( cType.Axioms!T() ) ) || ! cType.Axioms!T() ) { | |
static if(printMsgs) pragma(msg, T.stringof ~ " fails axioms of " ~ cType.stringof); | |
return false; | |
} | |
} else static if( hasMember!(T, member) ) { | |
alias CheckType = getMemberType!(T, member); | |
static if(isSomeFunction!(ConceptMemberType)) { | |
static if( ! is(CheckType : ConceptMemberType) | |
|| !is( getPropertyType!(cType, member) : getPropertyType!(T, member))) { | |
static if(printMsgs) | |
{ | |
pragma(msg, T.stringof ~ "." ~ member ~ " is not compatible with " ~ cType.stringof ~ "." ~ member); | |
pragma(msg, "\t\"" ~ CheckType.stringof ~ "\" vs. \"" ~ ConceptMemberType.stringof ~ "\""); | |
} | |
return false; | |
} | |
} else { | |
static if( ! is( getPropertyType!(cType, member) : getPropertyType!(T, member) ) ) { | |
static if(printMsgs) | |
{ | |
pragma(msg, T.stringof ~ "." ~ member ~ " is not compatible with " ~ cType.stringof ~ "." ~ member); | |
pragma(msg, "\t\"" ~ getPropertyType!(cType, member).stringof ~ "\" vs. \"" ~ getPropertyType!(T, member).stringof ~ "\""); | |
} | |
return false; | |
} | |
} | |
} else { | |
static if(printMsgs) | |
{ | |
pragma(msg, T.stringof ~ " lacks member \"" ~ member ~ "\" of type: "); | |
pragma(msg, "\t" ~ ConceptMemberType.stringof); | |
} | |
return false; | |
} | |
} | |
} | |
} | |
return true; | |
} | |
/** Prints error messages for why template instantiation failed. | |
--- | |
mixin(conceptDiagnostics!("TemplateName", Concept1, Concept2, Concept3)); | |
--- | |
*/ | |
template conceptDiagnostic(R, string F, int L, C...) | |
{ | |
import std.conv; | |
mixin conceptDiagnosticPrinter!(F ~ "(" ~ to!string(L) ~ "): ", R,C); | |
enum conceptDiagnostic = false; | |
static assert(false, "Template instantiation"); | |
} | |
//Template to assert on, so messages don't end up in wrong order. | |
private bool __writeMessage(string msg)() { | |
pragma(msg, msg); | |
return true; | |
} | |
template conceptDiagnosticPrinter(string prefix, R, C...) | |
{ | |
static if( C.length >= 1 ) { | |
static assert(__writeMessage!( prefix ~ "Concept failures for \"" ~ C[0].stringof ~ "\"")); | |
static assert( isConcept!(R,C[0], true) == false ); | |
static if (C.length > 1 ) { | |
alias conceptDiagnosticPrinter = conceptDiagnosticPrinter!(prefix, R,C[1..$]); | |
} else { | |
enum conceptDiagnosticPrinter = false; | |
} | |
} else { | |
enum conceptDiagnosticPrinter = false; | |
} | |
} | |
/** | |
Base class for Concepts. All Concepts should derive from this, or another concept. | |
*/ | |
class Concept {} | |
//Cannot be defined inside unittest. | |
class CInputRange(E) : Concept | |
{ | |
abstract void popFront(); | |
@property abstract E front(); | |
bool empty; | |
} | |
class COutputRange(E) : Concept | |
{ | |
static bool Axioms(R)() { | |
R r; | |
E e; | |
return _traits(__compiles( put(r,e))); | |
} | |
} | |
class CInfinite() : Concept | |
{ | |
static bool Axioms(T)() { | |
enum empty = T.empty; | |
return !empty; | |
} | |
} | |
class CInfiniteInputRange(E) : CInputRange!E | |
{ | |
mixin CInfinite; | |
} | |
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
import concepts; | |
struct StringRange | |
{ | |
@property string front() { return "Element";} | |
void popFront() {} | |
void extraFunction() {} | |
enum empty = true; | |
} | |
bool DoStuff(R)(R infRange) if ( isConcept!(R, CInfiniteInputRange!string, false)) | |
{ | |
return true; | |
} | |
bool DoStuff(R)(R infRange) if ( isConcept!(R, COutputRange!string, false)) | |
{ | |
return true; | |
} | |
bool DoStuff(R, string F = __FILE__, size_t L = __LINE__ )(R infRange) | |
{ | |
mixin conceptDiagnostic!(R, F, L, COutputRange!string, CInfiniteInputRange!string); | |
return false; | |
} | |
int main() | |
{ | |
DoStuff(StringRange()); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment