template<typename T>struct Example { typename T::SomeType t; }; struct Ok { typedef int SomeType; }; Example<Ok> ok; // perfectly fineIt should be fairly uncontroversial to point out that is not going to work with native types, such as int:
Example<int> i; // not so fineBut, if we were to provide an int-compatible instantiation, then the presence of the default instantiation isn't going to interfere with the use of the overridden version:
template<>struct Example<int> { int t; }; Example<int> i; // fine now, default Example template is no longer considered.This selection process can be used to choose programmatically between different template instantiations, based on the presence or absence of an embedded type (i.e. a tag type) in a type declaration. The syntax used to define these kinds of template mechanisms can often be somewhat opaque, so I devised a mechanism which conveniently wraps the type detection mechanism into a single macro, called TYPE_CHECK(). An example usage would be something like this:
TYPE_CHECK(Test1Check, T, TypeToCheck, static const bool VALUE = true, // Test1Check body when T::TypeToCheck exists static const bool VALUE = false); // Test1Check body defaultThis defines a template type called Test1Check<T>, containing a boolean constant VALUE which is true for any T where T::TypeToCheck exists, or false if it doesn't, so, in the following example, we would see output of "0, 1" from printf():
struct TestingF {}; struct TestingT { typedef void TypeToCheck; }; printf("%u, %u\n", // prints "0, 1" Test1Check<TestingF>::VALUE, Test1Check<TestingT>::VALUE);TYPE_CHECK() takes 5 arguments: the first and second are the name of the check type (Test1Check), and the type parameter (usually but not necessarily T). The macro will expand into a template struct definition (template<typename T>struct Test1Check { /*...*/ }; in this case). The third parameter is the name of the type we want to test for (i.e. the presence or absence of T::TypeToCheck), and the fourth and fifth parameters represent the body of this struct (the /*...*/ part) if the test type is present, or the default in the case it's not present.
We could rewrite our initial Example given above as follows, although it should now work for any type without an embedded T::SomeType, and not just int:
TYPE_CHECK(Example, T, SomeType, typename T::SomeType t, T t);You can also use TYPE_CHECK() to embed functions into the check type, so that your program can operate differently depending on if the test type is present or not. You can use this to implement some fairly primitive compile-time reflection mechanisms.
One additional refinement worth mentioning is that if you have a compiler which supports C99-style variadic macros, it's possible to parenthesize the fourth and fifth arguments, which is occasionally useful if they need to contain commas—an example of this is in the test code provided below.
There's one additional macro called TYPE_CHECK_FRIEND(). It takes the name of a check defined by TYPE_CHECK() and this can be placed inside the body of a type if you want to give the check access to the internals of a type. Again, there's an example of this in the test code.
The TYPE_CHECK() implementation lives in a single header file, nominally called "type_check.h", which can be copied from here. You should be able to just paste it to a local file and start using it. It contains the two macros outlined above, and a few implementation details (anything in the namespace tc_ or starting with a tc_ prefix), which you can ignore. If you're using a compiler which doesn't support variadic macros, you should #define TYPE_CHECK_NO_VA_ARGS before #including it.
A simple 'test suite' can be copied from here, which shows a few different ways that this kind of mechanism can be used. As far as I'm concerned, this code is public domain, so feel free to do whatever you'd like with it.