1 module concepts.models;
2 
3 /**
4  * A static assertion that a type satisfies a given template constraint.
5  * It can be used as a $(LINK2 ../attribute.html#uda, UDA) or
6  * in a `static assert` to make sure that a type conforms
7  * to the compile-time interface the user expects it to.
8  * The difference between using `models` and a simple static assert
9  * with the template contraint is that `models` will instantiate the
10  * failing code when the constraint is not satisfied,
11  * yielding compiler error messages to aid the user.
12  *
13  * The template contraint predicate must start with the word `is`
14  * (e.g. `isInputRange`) and an associated template function
15  * with the "is" replaced by "check" should exist (e.g. `checkInputRange`)
16  * and be defined in the same module.
17  */
18 template models(alias T, alias P, A...)
19 {
20     static assert(P.stringof[0..2] == "is",
21                   P.stringof ~ " does not begin with 'is', which is require by `models`");
22 
23     static if(P!(T, A))
24     {
25         bool models()
26         {
27             return true;
28         }
29     }
30     else
31     {
32         bool models()
33         {
34             import std.algorithm: countUntil;
35             import std.traits: moduleName;
36 
37             enum openParenIndex = P.stringof.countUntil("(");
38             enum untilOpenParen = P.stringof["is".length .. openParenIndex];
39             enum checkName = "check" ~ untilOpenParen;
40             enum mixinStr = checkName ~ "!(T, A);";
41             mixin("import " ~ moduleName!(P) ~ ";"); //make it visible first
42             mixin(mixinStr);
43             return false;
44         }
45     }
46 }
47 
48 
49 ///
50 unittest
51 {
52 
53     enum isFoo(T) = is(typeof(checkFoo!T));
54 
55 
56     template isBar(T, U)
57     {
58         enum isBar = is(typeof(checkBar!(T, U)));
59     }
60 
61     @models!(Foo, isFoo) //as a UDA
62     struct Foo
63     {
64         void foo() {}
65         static assert(models!(Foo, isFoo)); //as a static assert
66     }
67 
68     @models!(Bar, isBar, byte) //as a UDA
69     struct Bar
70     {
71         byte bar;
72         static assert(models!(Bar, isBar, byte)); //as a static assert
73     }
74 
75     // can't assert that, e.g. !models!(Bar, isFoo) -
76     // the whole point of `models` is that it doesn't compile
77     // when the template constraint is not satisfied
78     static assert(!__traits(compiles, models!(Bar, isFoo)));
79     static assert(!__traits(compiles, models!(Foo, isBar, byte)));
80 }
81 
82 
83 @safe pure unittest
84 {
85     struct Foo {}
86     enum weirdPred(T) = true;
87     //always true, this is a sanity check
88     static assert(weirdPred!Foo);
89     //shouldn't compile since weirdPred doesn't begin with the word "is"
90     static assert(!__traits(compiles, models!(Foo, weirdPred)));
91 }
92 
93 
94 @("@models can be applied to serialise")
95 @safe pure unittest {
96     static assert(__traits(compiles, models!(serialise, isSerialisationFunction)));
97     static assert(!__traits(compiles, models!(doesNotSerialise, isSerialisationFunction)));
98 }
99 
100 // FIXME
101 // @("@models can be applied to deserialise")
102 // @safe pure unittest {
103 //     static assert(__traits(compiles, models!(deserialise, isDeserialisationFunction)));
104 // }
105 
106 
107 version(unittest) {
108     void checkFoo(T)()
109     {
110         T t = T.init;
111         t.foo();
112     }
113 
114     void checkBar(T, U)()
115     {
116         U _bar = T.init.bar;
117     }
118 
119     void checkSerialisationFunction(alias F)() {
120         ubyte[] bytes = F(5);
121     }
122 
123     enum isSerialisationFunction(alias F) = is(typeof(checkSerialisationFunction!F));
124 
125     @models!(serialise, isSerialisationFunction)
126     ubyte[] serialise(T)(in T val) {
127         return [42];
128     }
129 
130     void doesNotSerialise(T)(in T val) {
131 
132     }
133 
134     void checkDeserialisationFunction(alias F)() {
135         ubyte[] bytes;
136         Request = F!int(bytes);
137     }
138     void isDeserialisationFunction(alias F) = is(typeof(isDeserialisationFunction!F));
139 
140     T deserialise(T)(in ubyte[] bytes) {
141         return T.init;
142     }
143 }