www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Concepts? Template specialization depending on class functions?

reply Ben Phillips <Ben_member pathlink.com> writes:
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'".
Mar 02 2006
next sibling parent Ben Phillips <Ben_member pathlink.com> writes:
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).
Mar 02 2006
prev sibling next sibling parent reply Ben Phillips <Ben_member pathlink.com> writes:
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?
Mar 02 2006
parent reply Oskar Linde <olREM OVEnada.kth.se> writes:
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
Mar 02 2006
parent Ben Phillips <Ben_member pathlink.com> writes:
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.
Mar 02 2006
prev sibling parent reply "Walter Bright" <newshound digitalmars.com> writes:
"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.
Mar 02 2006
parent reply Sean Kelly <sean f4.ca> writes:
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
Mar 02 2006
next sibling parent Sean Kelly <sean f4.ca> writes:
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
Mar 02 2006
prev sibling parent reply Ben Phillips <Ben_member pathlink.com> writes:
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.
Mar 02 2006
parent Sean Kelly <sean f4.ca> writes:
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
Mar 03 2006