Thread overview
Concepts? Template specialization depending on class functions?
Mar 02, 2006
Ben Phillips
Mar 02, 2006
Ben Phillips
Mar 02, 2006
Ben Phillips
Mar 02, 2006
Oskar Linde
Mar 03, 2006
Ben Phillips
Mar 02, 2006
Walter Bright
Mar 02, 2006
Sean Kelly
Mar 02, 2006
Sean Kelly
Mar 03, 2006
Ben Phillips
Mar 03, 2006
Sean Kelly
March 02, 2006
I have a question and a suggestion:
Is it possible to determine whether a given class contains a specific member
function?

It would be useful to be able to create a template for classes that have certain
functions without
requiring the user to inherit from an interface. One idea I've heard is to use
"concepts" (a possible
extension to C++) where code would go as follows

concept Collection(T)
{
bool add(T t);
bool isEmpty();
..
}

template doSomething(CollectionType : Collection)
{
void doSomething(CollectionType t) { ... }
}

This would allow the compiler to give a clearer error message to people who try
to use the template
with a class that doesn't fit the Collection concept. Instead of errors about
undefined functions the user
would be told something like "Error: T does not fit the Collection concept [and
maybe list the missing
functions'".


March 02, 2006
In article <du751p$1imp$1@digitaldaemon.com>, Ben Phillips says...
>
>I have a question and a suggestion:
>Is it possible to determine whether a given class contains a specific member
>function?
>
>It would be useful to be able to create a template for classes that have certain
>functions without
>requiring the user to inherit from an interface. One idea I've heard is to use
>"concepts" (a possible
>extension to C++) where code would go as follows
>
>concept Collection(T)
>{
>bool add(T t);
>bool isEmpty();
>..
>}
>
>template doSomething(CollectionType : Collection)
>{
>void doSomething(CollectionType t) { ... }
>}
>
>This would allow the compiler to give a clearer error message to people who try
>to use the template
>with a class that doesn't fit the Collection concept. Instead of errors about
>undefined functions the user
>would be told something like "Error: T does not fit the Collection concept [and
>maybe list the missing
>functions'".
>
>

I should probably have clarified that a class would not have to inherit from a concept to qualify for a template. It would only have to have functions (and possibly aliases) of the same names.

Concepts would also only be used to help specialize templates. It would be illegal to do something like

concept Queue { ... }

void foo()
{
Queue q = new QueueClassThatFitsConcept(); // error, concepts aren't types and
can't be instantiated. Use interfaces in this case
}

Also, most importantly, since concepts aren't types and classes that "fit" a concept don't inherit from them there is no virtual function overhead (actually a class doesn't even have to know the concepts it fits).


March 02, 2006
In article <du751p$1imp$1@digitaldaemon.com>, Ben Phillips says...
>
>Is it possible to determine whether a given class contains a specific member function?
>

From reading the docs it seems like I should be able to do this using "is", but the appropriate syntax is unclear and I can't seem to figure it out. Does "is" make it possible to test of a class has a certain member function and, if so, what is the correct syntax?


March 02, 2006
"Ben Phillips" <Ben_member@pathlink.com> wrote in message news:du751p$1imp$1@digitaldaemon.com...
> One idea I've heard is to use
> "concepts" (a possible
> extension to C++) where code would go as follows

Concepts are an interesting idea. As far as I know, nobody has implemented it for C++ so nobody knows if it really works or not.


March 02, 2006
Walter Bright wrote:
> "Ben Phillips" <Ben_member@pathlink.com> wrote in message news:du751p$1imp$1@digitaldaemon.com...
>> One idea I've heard is to use
>> "concepts" (a possible
>> extension to C++) where code would go as follows
> 
> Concepts are an interesting idea. As far as I know, nobody has implemented it for C++ so nobody knows if it really works or not. 

Now that static if is ignored for template expansion, concepts aren't as necessary as they once were.  For example, this is how I'd have implemented concepts in D before, which is fairly close to how it would be done in C++:

    struct DoesNiftyThingA {}
    struct DoesNiftyThingB {}
    struct DoesntDoAnything {}

    template testFeatures( T )
    {
        static if( /* test for A */ )
            alias DoesNiftyThingA testFeatures;
        else static if( /* test for B */ )
            alias DoesNiftyThingB testFeatures;
        else
            alias DoesntDoAnything testFeatures;
    }

    // specialization for A
    template conceptFn( T, T1 : DoesNiftyThingA = testFeatures!(T) )
    {
        static assert( is(testFeatures!(T) == DoesNiftyThingA );
        void conceptFn() {}
    }

    // specialization for B
    template conceptFn( T, T1 : DoesNiftyThingB = testFeatures!(T) )
    {
        static assert( is(testFeatures!(T) == DoesNiftyThingB );
        void conceptFn() {}
    }

    // default implementation
    template conceptFn( T )
    {
        static assert( is(testFeatures!(T) == DoesNiftyThingB );
        void conceptFn() {}
    }

But using the template parameter list is unwieldy and error-prone (this the static asserts).  However, the equivalent now could be:

    template conceptFn( T )
    {
        static if( /* test for A */ )
            void conceptFn() {}
        else static if( /* test for B */ )
            void conceptFn() {}
        else
            void conceptFn() {}
    }

And pragmas make it fairly easy to provide meaningful error messages if there is no default:

    template conceptFn( T )
    {
        static if( /* test for A */ )
            void conceptFn() {}
        else static if( /* test for B */ )
            void conceptFn() {}
        else
        {
            pragma( msg, "conceptFn is not defined for this type." );
            static assert( false );
        }
    }

Coupled with Don's template string magic, it should be fairly simple to improve the error message to include the supplied type name, etc.

So... while I think concepts may still be nice to have, I'm not sure there's a pressing need for them any longer.  In fact, I think I'm going to go back and rewrite my template code to use the "static if" method above, as most of it still uses the old C++ style.


Sean
March 02, 2006
Sean Kelly wrote:
>
>     // default implementation
>     template conceptFn( T )
>     {
>         static assert( is(testFeatures!(T) == DoesNiftyThingB );
>         void conceptFn() {}
>     }

paste error, the above should not contain an assert.  Also, my C++ style example even used "static if" in the "new" way.  A true representation would actually be slightly longer/more complex.


Sean
March 02, 2006
Ben Phillips wrote:

> In article <du751p$1imp$1@digitaldaemon.com>, Ben Phillips says...
>>
>>Is it possible to determine whether a given class contains a specific member function?
>>
> 
> From reading the docs it seems like I should be able to do this using "is", but the appropriate syntax is unclear and I can't seem to figure it out. Does "is" make it possible to test of a class has a certain member function and, if so, what is the correct syntax?

I'm not fully sure of the syntax, but this seems to work:

To test if a class T contains a method test:

is(typeof(T.test) == function)

Or conceptualized:

template Testable(T) {
        const Testable = is(typeof(T.test) == function));
}

One could wish that you could write something like:

is(typeof(T.test) == int function(T))

To make sure that the function signature is OK, but this does not seem to work.

Instead, the following works kind of but doesn't check the exact type of the arguments. To check if T contains a method with the signature similar to int test(int,int,int):

is(typeof((new T).test(1,1,1)) == int)

But will be true for e.g. a int test(real,real,real) too...

/Oskar
March 03, 2006
In article <du7qud$2ji7$1@digitaldaemon.com>, Sean Kelly says...
>Coupled with Don's template string magic, it should be fairly simple to improve the error message to include the supplied type name, etc.
>
>So... while I think concepts may still be nice to have, I'm not sure there's a pressing need for them any longer.  In fact, I think I'm going to go back and rewrite my template code to use the "static if" method above, as most of it still uses the old C++ style.
>
>
>Sean

While this is true, if you have a large interface to check for compatability
with, template code can still
become messy.


March 03, 2006
In article <du7sda$2kmq$1@digitaldaemon.com>, Oskar Linde says...
>
>Ben Phillips wrote:
>
>> In article <du751p$1imp$1@digitaldaemon.com>, Ben Phillips says...
>>>
>>>Is it possible to determine whether a given class contains a specific member function?
>>>
>> 
>> From reading the docs it seems like I should be able to do this using "is", but the appropriate syntax is unclear and I can't seem to figure it out. Does "is" make it possible to test of a class has a certain member function and, if so, what is the correct syntax?
>
>I'm not fully sure of the syntax, but this seems to work:
>
>To test if a class T contains a method test:
>
>is(typeof(T.test) == function)
>
>Or conceptualized:
>
>template Testable(T) {
>        const Testable = is(typeof(T.test) == function));
>}
>
>One could wish that you could write something like:
>
>is(typeof(T.test) == int function(T))
>
>To make sure that the function signature is OK, but this does not seem to work.
>
>Instead, the following works kind of but doesn't check the exact type of the arguments. To check if T contains a method with the signature similar to int test(int,int,int):
>
>is(typeof((new T).test(1,1,1)) == int)
>
>But will be true for e.g. a int test(real,real,real) too...
>
>/Oskar

Ah.. thanks! This helps a lot. You pointed out that your last method for testing
method signature and
arguments can be slightly ambiguous, but I doubt this will pose a problem in
most real world scenarios.


March 03, 2006
Ben Phillips wrote:
> In article <du7qud$2ji7$1@digitaldaemon.com>, Sean Kelly says...
>> Coupled with Don's template string magic, it should be fairly simple to improve the error message to include the supplied type name, etc.
>>
>> So... while I think concepts may still be nice to have, I'm not sure there's a pressing need for them any longer.  In fact, I think I'm going to go back and rewrite my template code to use the "static if" method above, as most of it still uses the old C++ style.
> 
> While this is true, if you have a large interface to check for compatability
> with, template code can still become messy.

Yup.  I briefly considered private functions with aliases inside the static if:

private implA() {}
private implB() {}

template conceptFn( T ) {
    static if( /* supports A */ )
        alias implA conceptFn;
    else if( /* supports B */ )
        alias implB conceptFn;
    else
    {   pragma( msg, "Not supported." );
        static assert( false );
    }
}

But I ultimately decided to simply format and comment things in a way that was as readable as possible.


Sean