Skip to content

Instantly share code, notes, and snippets.

@schancel
Created October 18, 2014 22:45
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/dc1f9f105dc2c3bc182a to your computer and use it in GitHub Desktop.
Save schancel/dc1f9f105dc2c3bc182a to your computer and use it in GitHub Desktop.
Concept diagnostic diagnostic messages
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;
}
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