The user customizes a library template class by defining a class specifying the desired options. Call it a manifest. The idea is to have optional typedefs in the manifest. If the user’s manifest contains a typedef for H, for example, I want the library code to use the indicated type as its “H”. If there is no typedef in the user’s manifest, the library will use a default.
I suspect there is an elegant way to do this by utilizing the new C++11 features, but I am coming up empty. I have a solution, based on the Wikipedia entry for SFINAE. It is ugly. It requires a new template function has_typedef_H for each new H. I am vaguely uneasy that it exploits the property that 0 can mean either the integer or a null pointer. Just seems too klugey.
Is there a better way? Preferably one that will work in VC++ 2010?
In the toy example there are five classes, H1, H2, and U0, U1, and U2. H1 and H2 are examples of “helpers” for a library class L. H1 is the default. The U’s are examples of user-defined classes. In the example, I omitted defining a library class L, just using the body of main() to select the H’s based upon typedefs in the U’s (or the lack thereof). subject of type H2.
struct H1{
void operator() (){ std::cout << "H1" << std::endl;}
};
struct H2{
void operator() (){ std::cout << "H2" << std::endl;}
};
struct default_H: public H1 {};
struct U2 {
typedef H2 H;
};
struct U1 {
typedef H1 H;
};
struct U0 {
};
template <typename T>
class has_typedef_H {
typedef char no[false+1];
typedef char yes[true+1];
template
static yes& test(typename C::H*);
template
static no& test(...);
public:
static const bool value = sizeof(test(0))-1;
};
template<typename U, bool >
struct type_H_B: public default_H{};
template<typename U>
struct type_H_B<U, true>: public U::H {};
template<typename U>
struct H_type: public type_H_B<U, has_typedef_H<U>::value> {};
int main() {
H_type<U0> h0;
H_type<U1> h1;
H_type<U2> h2;
// Prints H1 H1 H2
h0();
h1();
h2();
return 0;
}
You don’t really need to provide a complex trait for each one of the nested types, that can be done a bit simpler. Here is an example:
You can opt to provide a slightly simpler solution if the macro takes the default type to use and injects it in the default case (when the nested type is not defined). Admittedly, this requires the creation of the trait for each nested type, but the trait is just a couple of lines (and not too hard to define as a macro). Alternatively, if there is just a couple of potential typedefs you might be able to do without the extra boilerplate and use SFINAE directly on the destination type.
Completely different approach… if you can
If you can modify the types that are used in the libraries, then you can use a much simpler (although not so cool solution) by abusing inheritance. Create a base class that only holds the typedefs for the default types to be used, and have every user class derive from the class that offers the defaults. If the user wants to provide a better helper than the default they just need to provide the typedef. If they don’t provide a typedef, lookup will find the default higher up the hierarchy: