I am trying to provide users of a class (MyGizmo below) that derives from a variadic hierarchy (ObjGetter below) with a simple, uncluttered way to unambiguously call a member function that takes no arguments (check() below). I can make this work with functions that take arguments (like tune() below) but I have not found a way to make it work for functions that take no arguments.
struct Base { };
struct ObjA : public Base { };
struct ObjB : public Base { };
struct ObjC : public Base { };
template <class ... Obj> struct ObjGetter;
template <class Obj, class ... Tail>
struct ObjGetter<Obj, Tail ...> : public ObjGetter<Tail ...>
{
using ObjGetter<Tail ...>::tune; // resolve ambiguous lookups for tune()
void tune(Obj * obj) { } // no problem with this one, disambiguated by obj type
Obj * check() const { return 0; } // problem with this one, no arg to disambiguate
};
template <> struct ObjGetter<> { // to terminate the recursion
void tune(void); // needed by the using statement above but should not be used, hence different syntax
};
struct MyGizmo : public ObjGetter<ObjA, ObjC> // variadic
{
void testit() {
ObjA * a = 0; ObjB *b = 0; ObjC *c = 0;
a = ObjGetter<ObjA, ObjC>::check(); // too ugly!
c = ObjGetter<ObjC>::check(); // too ugly!
tune(a); // no problem
//tune(b); // correct compile-time error: no matching function for call to ‘MyGizmo::tune(ObjB*&)’
tune(c); // no problem
// I would like a simple syntax like this:
//a = check<ObjA>(); // should call ObjGetter<ObjA, ObjC>::check()
//b = check<ObjB>(); // should give a compile-time error
//c = check<ObjC>(); // should call ObjGetter<ObjC>::check()
}
};
I have tried the following but am not fully satistified:
First I can use a secondary, simply-templated class that gets carried around in the hierarchy, to reduce the ugly call to have just one template arg; yields something like:
a = ObjGetterHelper<ObjA>::check(); // still ugly! MyGizmo user should not have to know about ObjGetterCore
c = ObjGetterHelper<ObjC>::check(); // too ugly!
I can use a Type2Type helper and give check() an argument, this works fine, looks like this:
a = check(Type2Type<ObjA>()); // pretty ugly too
c = check(Type2Type<ObjC>()); // pretty ugly too
I could use macros but I don't want to go there...
#define CHECK(X) check(Type2Type<X>())
I think that template aliases will provide a solution but I am using g++ which does not support them yet. Is there anything else in the meantime? Thanks much!
You need a member function template check<Type> with some kind of structure to delegate up the inheritance chain if the type does not match the head of the variadic list.
This is a classic problem for SFINAE.
template< class Obj2 >
typename std::enable_if< std::is_same< Obj, Obj2 >::value, Obj * >::type
check() const { return 0; } // perform work
template< class Obj2 >
typename std::enable_if< ! std::is_same< Obj, Obj2 >::value, Obj2 * >::type
check() const { return base::template check< Obj2 >(); } // delegate
Works the same as my other answer. I'll leave that one as an example of baroque stupidity.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With