template Tuple(T...) { alias T Tuple; } // All conditions are alias parameters, and must contain a template // // template IsConditionMet(T) // // with two fields: const bool Met, and const char[] Reason (but the second is only required if the first is false). // These fields can be forwarded fields with implicit template properties. // // For the purposes of generating conforming types, only standard conditions are allowed. template Concept(Conditions...) { const char[] ConditionType = "Concept"; alias FindAll!("Method", Conditions) MethodConditions; alias FindAll!("Concept", Conditions) ConceptConditions; alias Conditions AllConditions; template IsConditionMet(T) { alias CheckAllConditions!(T, Conditions) IsConditionMet; } template CheckConditions(T) { static assert(IsConditionMet!(T).Met, IsConditionMet!(T).Reason); } const char[] MemberList = GenerateMembers!(MethodConditions); interface impl // : GenerateInheritanceList!(ConceptConditions) // This should work when bug #586 is fixed { mixin(MemberList); } } template Method(RetType, char[] name, ParamTypes...) { const char[] ConditionType = "Method"; const char[] Signature = RetType.stringof ~ " " ~ name ~ ParamTypes.stringof; template IsConditionMet(T) { char[] dummy; // This is here as a workaround for bug #1113 mixin(`const bool isMet = is(typeof(T.` ~ name ~ `(` ~ ConvertToText!(ParamTypes) ~ `)) : RetType);`); static if (isMet) const bool Met = true; else { const bool Met = false; const char[] Reason = "Type '" ~ T.stringof ~ "' has no method with signature '" ~ Signature ~ "'."; } } } template GenerateInheritanceList(Concepts...) { static if (Concepts.length == 0) alias Tuple!() GenerateInheritanceList; else { alias Concepts[0] MyConcept; alias MyConcept.impl TheImpl; alias Tuple!(TheImpl, GenerateInheritanceList!(Concepts[1..$])) GenerateInheritanceList; } } template GenerateMembers(Methods...) { static if (Methods.length == 0) const char[] GenerateMembers = ""; else const char[] GenerateMembers = Methods[0].Signature ~ ";\n" ~ GenerateMembers!(Methods[1..$]); } template FindAll(char[] type, Conditions...) { static if (Conditions.length == 0) alias Tuple!() FindAll; else static if (Conditions[0].ConditionType == type) alias Tuple!(Conditions[0], FindAll!(type, Conditions[1..$])) FindAll; else alias FindAll!(type, Conditions[1..$]) FindAll; } template ConvertToText(ParamTypes...) { static if (ParamTypes.length == 0) const char[] ConvertToText = ""; else static if (ParamTypes.length == 1) const char[] ConvertToText = ParamTypes[0].stringof ~ ".init"; else const char[] ConvertToText = ParamTypes[0].stringof ~ ".init, " ~ ConvertToText!(ParamTypes[1..$]); } template CheckAllConditions(T, Conditions...) { static if (Conditions.length == 0) alias CheckInfo!(true, "") CheckAllConditions; else static if (Conditions[0].IsConditionMet!(T).Met) alias CheckAllConditions!(T, Conditions[1..$]) CheckAllConditions; else alias CheckInfo!(false, Conditions[0].IsConditionMet!(T).Reason) CheckAllConditions; } // A compile-time 'struct' template CheckInfo(bool met, char[] reason) { const bool Met = met; const char[] Reason = reason; } ///////////////////////////////// TESTING ////////////////////// alias Concept!( // Defines a concept which requires two member methods: Method!(char[], "testMethod", int), // char[] testMethod(int) Method!(char[], "anotherMethod", bool) // char[] anotherMethod(bool) ) TestConcept; alias Concept!( // Defines a concept which TestConcept, // inherits another concept Method!(char[], "andAnotherMethod", double) // requires the member method char[] andAnotherMethod(double) ) InheritedConcept; // Examples which can be interface MyInterface { char[] testMethod(int val); } interface MySecondInterface : MyInterface { char[] anotherMethod(bool); } interface MyThirdInterface : MySecondInterface { char[] andAnotherMethod(double); } interface MyFourthInterface : MyInterface { char[] andAnotherMethod(double); } void main() { // alias TestConcept.CheckConditions!(MyInterface) TheCheck; // Fails, and caught // alias InheritedConcept.CheckConditions!(MyThirdInterface) TheCheck2; // Works // alias InheritedConcept.CheckConditions!(MySecondInterface) TheCheck3; // Fails, and caught // alias InheritedConcept.CheckConditions!(MyFourthInterface) TheCheck4; // Fails, and caught } void ExampleFunc(T)(T t) { alias TestConcept.CheckConditions!(T) TheCheck; char[] foo = t.testMethod(1); // dchar[] foo2 = t.anotherMethod(true); // Mistake -- but caught by __ExampleFunc_test } alias ExampleFunc!(TestConcept.impl) __ExampleFunc_test;