Concepts vs template constraints - the practical approach

Ali Çehreli acehreli at yahoo.com
Sun Nov 20 10:47:39 PST 2011


On 11/20/2011 08:41 AM, Norbert Nemec wrote:
 > Hi there,
 >
 > back in the discussions about C++-"concepts" it was argued that
 > D-template-parameter constraints allow you to achieve the same goal.

Have a look at std.range.hasLength, std.range.isInputRange, and friends.

 > Now, I find it fairly difficult to come up with a clean solution for
 > this that actually scales up for complex libraries. My best attempt so
 > far is as follows:
 >
 > ===================================================
 >
 > template verifyMyConcept(A) {
 > static assert(is(A.type));
 > static assert(A.len >= 0);
 > static assert(is(typeof(A.init[0]) == A.type));
 > }
 >
 > struct MyClass(T,int R) {
 > alias T type;
 > enum len = R;
 >
 > T[R] value;
 >
 > T opIndex(int idx) {
 > return value[idx];
 > }
 >
 > mixin verifyMyConcept!(typeof(this));
 > }
 >
 > void myfunction(A)(A arr)
 > if(__traits(compiles, verifyMyConcept!(A)))
 > {
 > }
 >
 > unittest {
 > MyClass!(int,4) x;
 >
 > mixin verifyMyConcept!(typeof(x));
 > myfunction(x);
 > }
 >
 > ===================================================
 >
 > As you can see, this approach attempts to define all the requirements
 > for MyConcept in one place as individual static assertions. This permits
 > error messages to identify which requirement for the concept is not met.
 >
 > Still the code seems fairly ugly to me and the error message is not
 > quite clear enough for my taste.
 >
 > Ideally, there should be a way to use "concepts" similar to interfaces:
 >
 > a) A concept should be defined readably in one place, listing a set of
 > requirements, possibly inheriting other concepts.

I have separated the parts of the concepts below and then defined 
matchesMyConcept to "inherit" them.

 > b) A struct implementing the concept should state this in a similar way
 > to a class that implements an interface.
 >
 > c) A template that requires a parameter to fulfil a concept should state
 > this in a similar way to a function requiring a specific input type
 >
 > and most importantly:
 >
 > d) a user of the library should get a clear and simple error message
 > when using templates with parameters that do not fulfil the required
 > concept.

Although the following code works acceptably with dmd 2.056, sometimes 
the error messages are less than ideal. This may happen when there are 
overloads of a function template and none of them accept a template 
parameter. The compiler can only say that "there is no function template 
for this use".

 >
 > Has anyone achieved these goals better than my feeble attempt?
 >
 > Greetings,
 > Norbert

Here is something:

template hasType(T)
{
     enum bool hasType = is(T.type);
}

template hasNonNegativeLength(T)
{
     enum bool hasNonNegativeLength = T.len >= 0;
}

template firstElementSameType(T)
{
     enum bool firstElementSameType = is(typeof(T.init[0]) == T.type);
}

template matchesMyConcept(T)
{
     enum bool matchesMyConcept = (hasType!T &&
                                   hasNonNegativeLength!T &&
                                   firstElementSameType!T);
}

struct MyClass(T,int R) {
    alias T type;
    enum len = R;

    T[R] value;

    T opIndex(int idx) {
       return value[idx];
    }
}

struct YourClass
{}

void myfunction(A)(A arr)
     if (matchesMyConcept!A)
{
}

unittest {
    MyClass!(int,4) x;
    myfunction(x);     // <-- this works fine

    YourClass y;
    myfunction(y);     // <-- compilation ERROR for this one
}

void main()
{}

Ali



More information about the Digitalmars-d mailing list