Better pattern for partial specialization disambiguation precedence chain? - c++

Consider the following series of partial specializations:
template <typename T, typename Enable=void>
struct foo {
void operator()() const { cout << "unspecialized" << endl; }
};
template <typename T>
struct foo<T, enable_if_t<
is_integral<T>::value
>>{
void operator()() const { cout << "is_integral" << endl; }
};
template <typename T>
struct foo<T, enable_if_t<
sizeof(T) == 4
and not is_integral<T>::value
>>{
void operator()() const { cout << "size 4" << endl; }
};
template <typename T>
struct foo<T, enable_if_t<
is_fundamental<T>::value
and not (sizeof(T) == 4)
and not is_integral<T>::value
>>{
void operator()() const { cout << "fundamental" << endl; }
};
// etc...
Live Demo
I see this kind of thing all of the time (indeed, another StackOverflow answer elsewhere gives the same pattern for a similar problem). While this works, this code has some serious maintainability issues, and also precludes, e.g., user-level partial specializations at higher priority if the above code is in a library. What's a better pattern for expressing this idea? I feel like there has to be something (maybe involving inheritance and variadic template parameters?) that can express this idea more cleanly and maintainably. (Suppose also that each of the specializations is a full-on class rather than a simple functor, so overloaded functions don't work in a simplistic way).

The overgrowth of condition count can be solved by helper structs:
#include <iostream>
#include <type_traits>
using namespace std;
template <bool ThisCondition, class ParentCondition = void, class = void>
struct condition_resolver {
static constexpr bool is_condition_resolver = true;
static constexpr bool parent_condition_v = !ThisCondition;
static constexpr bool value = ThisCondition;
};
template <bool ThisCondition, class ParentCondition>
struct condition_resolver<ThisCondition, ParentCondition, enable_if_t<ParentCondition::is_condition_resolver> > {
static constexpr bool is_condition_resolver = true;
static constexpr bool parent_condition_v = !ThisCondition && ParentCondition::parent_condition_v;
static constexpr bool value = ThisCondition && ParentCondition::parent_condition_v;
};
template <typename T, typename Enable=void>
struct foo {
void operator()() const { cout << "unspecialized" << endl; }
};
template <typename T>
struct is_integral_foo: condition_resolver<is_integral<T>::value> { };
template <typename T>
struct foo<T, enable_if_t<is_integral_foo<T>::value>>{
void operator()() const { cout << "is_integral" << endl; }
};
template <typename T>
struct has_size_four_foo: condition_resolver<sizeof(T) == 4, is_integral_foo<T>> { };
template <typename T>
struct foo<T, enable_if_t< has_size_four_foo<T>::value>>{
void operator()() const { cout << "size 4" << endl; }
};
template <typename T>
struct is_fundamental_foo: condition_resolver<is_fundamental<T>::value, has_size_four_foo<T>> { };
template <typename T>
struct foo<T, enable_if_t<is_fundamental_foo<T>::value>>{
void operator()() const { cout << "fundamental" << endl; }
};
typedef char four_sized[4];
int main() {
foo<int>()();
foo<four_sized>()();
foo<nullptr_t>()();
}
Output:
is_integral
size 4
fundamental
PS.
Have in mind that void which is also fundamental will cause compiler to produce a warning that sizeof(void) is considered...
Edit:
If you really need to use specialization for solving the overgrowth of condition problem this might interest you:
#include <iostream>
#include <type_traits>
using namespace std;
template <class Tag, int Level, class... Args>
struct concrete_condition_resolver;
template <class Tag, int Level, class... Args>
struct condition_resolver;
template <class ConditionResolver>
struct condition_resolver_parent {
template<class CR = ConditionResolver>
constexpr enable_if_t<CR::level != 0, bool> operator()(bool parent) {
return (!parent && static_cast<const ConditionResolver*>(this)->condition && typename ConditionResolver::LevelUp()(true)) ||
(parent && !static_cast<const ConditionResolver*>(this)->condition && typename ConditionResolver::LevelUp()(true));
}
template<class CR = ConditionResolver>
constexpr enable_if_t<CR::level == 0, bool> operator()(bool parent) {
return (!parent && static_cast<const ConditionResolver*>(this)->condition) ||
(parent && !static_cast<const ConditionResolver*>(this)->condition);
}
};
template <class Tag, int Level, class... Args>
struct condition_resolver: concrete_condition_resolver<Tag, Level, Args...>, condition_resolver_parent<condition_resolver<Tag, Level, Args...>> {
using LevelUp = condition_resolver<Tag, Level - 1, Args...>;
using tag = Tag;
static constexpr int level = Level;
constexpr condition_resolver() {}
};
struct foo_tag { };
template <class First, class... Args>
struct concrete_condition_resolver<foo_tag, 0, First, Args...> {
static constexpr bool condition = is_integral<First>::value;
};
template <class First, class... Args>
struct concrete_condition_resolver<foo_tag, 1, First, Args...> {
static constexpr bool condition = sizeof(First) == 4;
};
template <class First, class... Args>
struct concrete_condition_resolver<foo_tag, 2, First, Args...> {
static constexpr bool condition = is_fundamental<First>::value;
};
template <typename T, typename = void>
struct foo;
template <typename T>
struct foo<T, enable_if_t<condition_resolver<foo_tag, 0, T>()(false)>>{
void operator()() const { cout << "is_integral" << endl; }
};
template <typename T>
struct foo<T, enable_if_t<condition_resolver<foo_tag, 1, T>()(false)>>{
void operator()() const { cout << "size 4" << endl; }
};
template <typename T>
struct foo<T, enable_if_t<condition_resolver<foo_tag, 2, T>()(false)>>{
void operator()() const { cout << "is_fundamental" << endl; }
};
typedef char four_sized[4];
int main() {
foo<int>()();
foo<four_sized>()();
foo<nullptr_t>()();
}
This approach is applicable even for overload functions using enable_if, while partial specialization deals only with structs...

Why am I answering my own question
So I've been bugged by this ever since asking this question, and I was never completely satisfied with the original answer. After much fiddling and trial/error, I've come up with a pattern I'm much happier with that uses tag dispatch. Whether or not it's actually better, more readable, and more maintainable than the previous answer is for you to judge, but I like it better. Feel free to pick it apart, criticize it, and break it. :-)
The Basic Version
Without further ado, here's the code that solve the simplest version of the problem
template <typename> struct always_true : true_type { };
template <typename> struct always_false : false_type { };
template <typename T, template <class...> class condition=always_false,
typename flag=integral_constant<bool, condition<T>::value>
>
struct foo;
////////////////////////////////////////
// "unspecialized" version
// put always_true and false_type together here so that no one gets here accidentally
template <typename T, typename true_or_false_type>
struct foo<T, always_true, true_or_false_type> {
void operator()() const { cout << "unspecialized" << endl; }
};
////////////////////////////////////////
// is_fundamental
template <typename T>
struct foo<T, is_fundamental, true_type> {
void operator()() const { cout << "is_fundamental" << endl; }
};
template <typename T> struct foo<T, is_fundamental, false_type> : foo<T, always_true> { };
////////////////////////////////////////
// is_integral
template <typename T>
struct foo<T, is_integral, true_type> {
void operator()() const { cout << "is_integral" << endl; }
};
template <typename T>
struct foo<T, is_integral, false_type> : foo<T, is_fundamental> { };
////////////////////////////////////////
// sizeof(T) == 4
template <typename T>
using size_is_4 = integral_constant<bool, sizeof(T) == 4>;
template <typename T>
struct foo<T, size_is_4, true_type> {
void operator()() const { cout << "size_is_4" << endl; }
};
template <typename T>
struct foo<T, size_is_4, false_type> : foo<T, is_integral> { };
////////////////////////////////////////
// Now put the most specialized condition in the base of this template
template <typename T, typename true_or_false_type>
struct foo<T, always_false, true_or_false_type> : foo<T, size_is_4> { };
The chain of precedence, held in a helper struct in the previous answer, is encoded in inheritance.
More bells and whistles
Adding the ability to enable user partial specializations with higher precedence than the library ones takes a little more doing, but the principle is the same. The full version in this demo.

Related

How to tell if template type is an instance of a non-variadic template class?

This question is awful similar to How to tell if template type is an instance of a template class?
I would like to detect if a template parameter is from one particular template class that has no variadic template arguments.
template<class U, class S>
struct A{};
template<class T>
struct B {
B() {
if constexpr (T == A) {
// T is a template instantiation of `A`.
} else {
}
}
};
I can't change A's definition. I can change B's definition to have additional template parameters.
How do I implement (T == A) given the restriction of not knowing A's U and S?
I would go for a partial specialization here.
#include <iostream>
template<class U, class S>
struct A{};
template<class T>
struct B {
B() {
std::cout << "None-A implementation\n";
}
};
template<class U, class S>
struct B<A<U, S>> {
B() {
std::cout << "A implementation\n";
}
};
int main() {
B<int> b1;
B<A<int, int>> b2;
}
You have the option of leaving the default-case without an implementation.
Or you can have a fallback implementation for any none-A classes like here.
If the partial specialization forces too much code duplication you can also extract the detection part to it's own template variable like this.
#include <iostream>
template<class U, class S>
struct A{};
template <class T>
constexpr bool is_A_instatiation = false;
template <class U, class S>
constexpr bool is_A_instatiation<A<U, S>> = true;
template<class T>
struct B {
B() {
if constexpr (is_A_instatiation<T>) {
std::cout << "A instatiation!\n";
} else {
std::cout << "none-A instatiation!\n";
}
}
};
int main() {
B<int> b1;
B<A<int, int>> b2;
}
The easiest way is:
template<class T>
struct B{/*default implementation*/};
template<class U,class S>
struct B<A<U,S>>{/*Specified implementation*/};
A<T,U>: you already know it and search key
B<...>: variadic types which may include A<T,U> - known type
And you want to search A<T,U> in B<...>
template <typename T, typename U>
struct A {};
template <typename T, typename U, typename ...Ts>
struct B {
static constexpr bool value = ((std::is_same_v< A<T, U>, Ts> || ... ));
};
int main() {
std::cout << std::boolalpha <<
B<int,float, int, int, float, A<int,float>>::value << '\n'<<
B<int,float, int, int, float>::value <<std::endl;
}

Template : class specialization

I'm new in the C++ world.
Sorry for my nooby question.
I have a class
template <typename T>
class Foo
{
T t_;
void say_hello()
{ std::cout << "Ciao";}
// work with T ...
};
I want to specialize this template class for 3 types.
If type is (A or B or C), Then use this class
template<>
class Foo<A or B or C>
{
void say_hello()
{ std::cout << "Hello";}
};
What's the best way to do this?
Thank you for your help.
A possible solution uses SFINAE
template <typename T, typename = void>
class Foo
{
T t_;
void say_hello()
{ std::cout << "Ciao";}
// work with T ...
};
template <typename T>
class Foo<T, std::enable_if_t<std::is_same_v<T, A>,
|| std::is_same_v<T, B>,
|| std::is_same_v<T, C>>
{
void say_hello()
{ std::cout << "Hello";}
};
If you don't use T inside the Foo specialization (as in your example) you can also use a sort of self-inheritance
template <typename T>
class Foo
{
T t_;
void say_hello()
{ std::cout << "Ciao";}
// work with T ...
};
template <>
class Foo<A>
{
void say_hello()
{ std::cout << "Hello";}
};
template <>
class Foo<B> : public Foo<A>
{ };
template <>
class Foo<C> : public Foo<A>
{ };
Off Topic: if you want to use say_hello() outside the class, is better if you make it public (or if you declare Foo as a struct).
There are several possibilities, for example:
Specialization of the method only:
template<>
void Foo<A>::say_hello() { std::cout << "Hello"; }
template<>
void Foo<B>::say_hello() { std::cout << "Hello"; }
template<>
void Foo<C>::say_hello() { std::cout << "Hello"; }
or, in C++17, you might do:
template <typename T>
class Foo
{
T t_;
void say_hello()
{
if constexpr(std::is_same_v<T, A> || std::is_same_v<T, B> || std::is_same_v<T, C>) {
std::cout << "Hello";
} else {
std::cout << "Ciao";
}
}
// work with T ...
};
Whereas regular if works in that example, it would fail if you call code specific to A, B, C.
if constexpr won't have that issue.
A variant of the SFINAE solution that is a bit more concise for more classes.
template<class T, class... Ts>
struct is_one_of;
template<class T, class Ts>
struct is_one_of<T, T, Ts...> : std::true_type {}; //maybe add std::decay_t
template<class T, class S, class Ts>
struct is_one_of<T, S, Ts...> : is_one_of<T, Ts...> {};
template<class T>
struct is_one_of<T> : std::false_type{};
template<class T, class... Ts>
constexpr bool is_one_of_v = is_one_of<T, Ts...>::value;
template <typename T, typename = void>
class Foo
{
T t_;
void say_hello()
{ std::cout << "Ciao";}
// work with T ...
};
template <typename T>
class Foo<T, std::enable_if_t<is_one_of_v<T, A, B, C>
{
void say_hello()
{ std::cout << "Hello";}
};

c++03: Mutually exclusive methods thanks to enable_if

Within a class, I have two different methods which should be mutually exclusive depending on the caller template parameter.
class Foo
{
// For collections
template<class T>
typename boost::enable_if<boost::is_same<typename std::vector<typename T::value_type>, T>::value, const T&>::type
doSomething()
{ }
// For single types
template<class T>
typename boost::enable_if<!boost::is_same<typename std::vector<typename T::value_type>, T>::value, const T&>::type
doSomething()
{ }
}
This won't compile.
error: type/value mismatch at argument 1 in template parameter list for 'template struct boost::enable_if'
error: expected a type, got '! boost::is_same::value'
How about:
template <typename T> struct is_std_vector : std::false_type {};
template <typename T, typename A>
struct is_std_vector<std::vector<T, A>> : std::true_type {};
And then
class Foo
{
// For collections
template<class T>
typename std::enable_if<is_std_vector<T>::value, const T&>::type
doSomething();
// For single types
template<class T>
typename std::enable_if<!is_std_vector<T>::value, const T&>::type
doSomething();
};
Unlike std's version, boost::enable_if accepts a type (kinda wrapper under boolean value), so you should write something like
class Foo
{
// For collections
template<class T>
typename boost::enable_if<
typename boost::is_same<typename std::vector<typename T::value_type>, T>,
const T&>::type doSomething()
{ }
// For single types
template<class T>
typename boost::enable_if_с<
!boost::is_same<typename std::vector<typename T::value_type>, T>::value,
const T&>::type doSomething()
{ }
}
Note here, I've used typename before boost::is_same and haven't used ::value in the first specification. On the contrary, I had to use enable_if_с in the second overload, because ! operator isn't applicable to a type.
What about a sort of tag dispatching?
#include <vector>
#include <iostream>
template <typename, typename>
struct isSame
{ typedef int type; };
template <typename T>
struct isSame<T, T>
{ typedef long type; };
struct foo
{
template <typename T>
T const & doSomething (T const & t, int)
{ std::cout << "int version" << std::endl; return t; }
template <typename T>
T const & doSomething (T const & t, long)
{ std::cout << "long version" << std::endl; return t; }
template <typename T>
T const & doSomething (T const & t)
{ return doSomething(t, typename isSame<
typename std::vector<typename T::value_type>, T>::type()); }
};
int main ()
{
foo f;
std::vector<int> v;
f.doSomething(v); // print "long version"
}
If what you want is to overload the function based on whether you are given a vector or not
#include <type_traits>
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
class Foo {
public:
// For collections
template <class T>
const vector<T>& do_something(const std::vector<T>& input) {
cout << __PRETTY_FUNCTION__ << endl;
return input;
}
// For single types
template <class T>
const T& do_something(const T& input) {
cout << __PRETTY_FUNCTION__ << endl;
return input;
}
};
int main() {
auto foo = Foo{};
auto v = std::vector<int>{};
auto i = int{};
foo.do_something(v);
foo.do_something(i);
}
If you want to be even more general and check for any instantiated type
#include <type_traits>
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
namespace {
template <typename T, template <typename...> class TT>
struct IsInstantiationOf
: public std::integral_constant<bool, false> {};
template <template <typename...> class TT, typename... Args>
struct IsInstantiationOf<TT<Args...>, TT>
: public std::integral_constant<bool, true> {};
} // namespace anonymous
class Foo {
public:
// For collections
template <typename VectorType, typename std::enable_if_t<IsInstantiationOf<
std::decay_t<VectorType>, std::vector>::value>* = nullptr>
void do_something(VectorType&&) {
cout << "Vector overload" << endl;
}
// For single types
template <class T, typename std::enable_if_t<!IsInstantiationOf<
std::decay_t<T>, std::vector>::value>* = nullptr>
void do_something(T&&) {
cout << "Non vector overload" << endl;
}
};
int main() {
auto foo = Foo{};
auto v = std::vector<int>{};
auto i = int{};
foo.do_something(v);
foo.do_something(i);
}
Also please note that you should avoid putting std::enable_if in the function signature as much as possible for these reasons https://stackoverflow.com/a/14623831/5501675

Modify SFINAE idiom to check return type of a function using std::is_arithmetic

I am using SFINAE idiom to check if a type has a method (some_method()) with a given signature defined:
template <typename... Other>
struct has_method {
static constexpr bool value = has_method<Other...>::value;
};
template <typename U, typename... Other>
struct has_method<U, Other...> {
static constexpr bool value =
has_method<U>::value && has_method<Other...>::value;
};
template <typename U>
struct has_method<U> {
template <typename T, T>
struct helper;
template <typename T>
static std::uint8_t check(helper<int (*)(size_t), &T::some_method>*);
template <typename T>
static std::uint16_t check(...);
static constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t);
};
I would like to modify it to check if the return type of the function is arithmetic type, using std::is_arithmetic. Would that be possible?
I have tried modifying it using static_assert() together with std::is_arithmetic< decltype(((T*)nullptr)->foo())>::value but didn't succeed.
EDIT: I've updated the answer to allow for the given method to take arbitrary arguments, not simply have a void signature
I think this will do the trick:
namespace details {
template<typename...>
struct voider
{
using type=void;
};
template<typename T>
struct GetReturnType;
// non member function
template<typename Ret, typename... Args>
struct GetReturnType<Ret(*)(Args...)>
{
using type = Ret;
};
// mmeber function
template<typename Ret, typename C, typename... Args>
struct GetReturnType<Ret(C::*)(Args...)>
{
using type = Ret;
};
}
template<typename...Ts>
using void_t = typename details::voider<Ts...>::type;
template<typename T, typename=void>
struct has_arithmetic_method : std::false_type{};
template<typename T>
struct has_arithmetic_method<T, void_t<decltype(&T::some_method)>> : std::is_arithmetic<typename details::GetReturnType<decltype(&T::some_method)>::type>::type{};
I'm using the voider concept to make sure that accessing some_method on T is well-formed, and then deriving from std::is_arithmetic if it is. I had to add a helper to pull off the return type of an arbitrary function.
Some structs to test against. Notice how the ones that should pass take in different arguments for some_method:
struct FailSomeMethod // lacks a some_method altogether
{
void some_other_method()
{
std::cout << "FailSomeMethod::some_other_method" << std::endl;
}
};
struct PassSomeMethod // has a some_method, but it's not arithmetic
{
void some_method()
{
std::cout << "PassSomeMethod::some_method" << std::endl;
}
};
struct PassArithmeticMethod
{
int some_method(int _a)
{
std::cout << "PassArithmeticMethod::some_method" << std::endl;
return 1;
}
};
struct PassArithmeticMethod2
{
double some_method()
{
return 1.0;
}
};
struct PassArithmeticMethod3
{
float some_method(int a, double b, float c, char d, bool e)
{
return 1.;
}
};
The actual test:
int main()
{
//test the has_arithmetic_method concept
std::cout << "Struct PassArithmeticMethod: " << std::boolalpha << has_arithmetic_method<PassArithmeticMethod>::value << std::endl;
std::cout << "Struct PassSomeMethod: " << std::boolalpha << has_arithmetic_method<PassSomeMethod>::value << std::endl;
std::cout << "Struct FailSomeMethod: " << std::boolalpha << has_arithmetic_method<FailSomeMethod>::value << std::endl;
std::cout << "Struct PassArithmeticMethod2: " << std::boolalpha << has_arithmetic_method<PassArithmeticMethod2>::value << std::endl;
std::cout << "Struct PassArithmeticMethod3: " << std::boolalpha << has_arithmetic_method<PassArithmeticMethod3>::value << std::endl;
}
Outputs:
Struct PassArithmeticMethod: true
Struct PassSomeMethod: false
Struct FailSomeMethod: false
Struct PassArithmeticMethod2: true
Struct PassArithmeticMethod3: true
Live Demo
Honestly, despite the (misleading) length of my answer, I feel that this is the simplest. The voider concept and the GetReturnType helper are general purpose structs that can be easily re-used. I don't use any constexpr in my code; I leave all the work for getting true and false to inheritance tricks.
I think this is what you are looking for:
struct Unknown {};
template <typename T_>
struct Wrap
{
typedef T_ type;
};
template <class C_, typename... A_>
struct HasMethodArithmeticReturn
{
template <class U_>
static Wrap<decltype(std::declval<U_>().NAME(std::declval<A_>()...))> test(void*);
template <class U_>
static Unknown test(...);
typedef char yes[1];
typedef char no[2];
template <typename>
struct helper
{
typedef no type;
};
template <typename R_>
struct helper<Wrap<R_>>
{
template <class U_, R_(U_::*)(A_...)>
struct assist
{
typedef yes type;
};
template <class U_>
static typename std::enable_if<std::is_arithmetic<R_>::value, typename assist<U_, &U_::NAME>::type>::type& test(void*);
template <class U_>
static no& test(...);
typedef decltype(test<C_>(0)) type;
};
static const bool value = sizeof(yes) == sizeof(typename helper<decltype(test<C_>(0))>::type);
};
You can see it used live. If the class has a method with A_... parameters and an arithmetic return type, value is true. Otherwise, it is false.
In addition to #James Root's answer, which allows specification of the arguments but doesn't allow the user to test multiple classes at the same time as in the OP, you may also do the following:
#include <type_traits>
template <typename... Other>
struct has_method {
static constexpr bool value = has_method<Other...>::value;
};
template <typename U, typename... Other>
struct has_method<U, Other...> {
static constexpr bool value =
has_method<U>::value && has_method<Other...>::value;
};
template <typename U>
struct has_method<U> {
template <typename T>
static auto typeget(int) -> decltype(T::some_method(size_t{}));
template <typename T>
static void typeget(...);
static constexpr bool value =
std::is_arithmetic<decltype(has_method::typeget<U>(0))>::value;
};
The implementation syntax is simpler, but you pay for it in other ways: you have to make a new template for every set of argument types you wish to supply to the function, and this detects whether the function may be called with the parameters, not whether the function has the exact desired parameters.
Live on Coliru, though I've replaced size_t with uint8_t to demonstrate that if the desired type can be implicitly converted to the argument type in the member function of the queried type, then the expression evaluates as true.

How to find, from which template-layers is object composed of?

How can I use templates, to find out, from which types is type composed of when using template layers?
Let's have
template <typename Super>
class A : public Super {};
template <typename Super>
class B : public Super {};
template <typename Super>
class C : public Super {};
class Blank{};
template <typename CombinedType>
void printTypeComponents(const CombinedType & t) { ... }
int main()
{
typedef A<B<C<Blank>>> ComposedType;
ComposedType ct;
printTypeComponents(ct);
typedef A<C<Blank>> ComposedType2;
ComposedType2 ct2;
printTypeComponents(ct2);
}
I am attaching my try, wrong of course (works only if object is composed from all tested types, since tested types actually exists), but you can easily see from it, what my aim is
#include <boost/type_traits/is_base_of.hpp>
#include <iostream>
template <typename Super>
class A : public Super
{
public:
typedef A<Super> AComponent;
};
template <typename Super>
class B : public Super
{
public:
typedef B<Super> BComponent;
};
template <typename Super>
class C : public Super
{
public:
typedef C<Super> CComponent;
};
class Blank{};
template <typename CombinedType>
void printTypeComponents(const CombinedType & t)
{
if(boost::is_base_of<Blank, CombinedType::AComponent>::value)
std::cout << "composed of A \n";
if(boost::is_base_of<Blank, CombinedType::BComponent>::value)
std::cout << "composed of B \n";
if(boost::is_base_of<Blank, CombinedType::CComponent>::value)
std::cout << "composed of C \n";
}
int main()
{
typedef A<B<C<Blank>>> ComposedType;
ComposedType ct;
printTypeComponents(ct);
//typedef A<C<Blank>> ComposedType2;
//ComposedType2 ct2;
//printTypeComponents(ct2);
}
I am using MSVC2010
Thank you!
EDIT:
I am not actually interested in names of types... I want to use it like:
if(composedOfA)
doSomeCharacteristicStuffFromA(); //member function of A
if(composedOfB)
doSomeCharacteristicStuffFromB(); //member function of B
My attempt (without using C++0x feature(s))
//----------------------------------------
struct null{};
template<typename>
struct split
{
typedef null Ct;
typedef null At;
};
template<template<typename> class C, typename T>
struct split<C<T> >
{
typedef C<null> Ct; //class template
typedef T At; //argument type
};
template<template<typename> class C>
struct split<C<Blank> >
{
typedef C<null> Ct; //class template
typedef Blank At; //argument type
};
template<typename T, typename U>
struct is_same
{
static const bool value = false;
};
template<typename T>
struct is_same<T,T>
{
static const bool value = true;
};
typedef A<null> anull;
typedef B<null> bnull;
typedef C<null> cnull;
//----------------------------------------
template <typename CombinedType>
void printTypeComponents(const CombinedType & t)
{
typedef typename split<CombinedType>::Ct Ct;
typedef typename split<CombinedType>::At At;
if ( is_same<Ct,anull>::value )
cout << "A" << endl;
else if ( is_same<Ct,bnull>::value )
cout << "B" << endl;
else if ( is_same<Ct,cnull>::value )
cout << "C" << endl;
if ( !is_same<At,Blank>::value )
printTypeComponents(At());
else
cout << "Blank" << endl;
}
Test code:
int main()
{
typedef A<B<C<Blank> > > ComposedType;
ComposedType ct;
printTypeComponents(ct);
cout<<"-------"<<endl;
typedef A<C<Blank> > ComposedType2;
ComposedType2 ct2;
printTypeComponents(ct2);
}
Output:
A
B
C
Blank
-------
A
C
Blank
Online Demo : http://ideone.com/T5nD4
Exploiting your structure, what about:
template <template <typename> class X, typename T>
void print_type(const X<T>& x, char (*)[std::is_base_of<T, X<T>>::value] = 0)
{
std::cout << "Base: " << typeid(T).name() << "\n";
print_type<T>(x);
}
template <typename T>
void print_type(const T&) {}
Here is a template unraveller that uses variadic typenames. You can probably make it work in VS2010 with the usual macro tricks (e.g. like in the pretty-printer.)
template <typename T> class A : public T {};
template <typename T> class B : public T {};
template <typename T> class C : public T {};
struct NullType {};
#include <tuple>
#include <iostream>
template <typename ...Args> struct Concat;
template <typename T, typename ...Args>
struct Concat<T, std::tuple<Args...>>
{
typedef std::tuple<T, Args...> type;
};
template <typename> struct Unravel;
template <typename T, template <typename> class X>
struct Unravel<X<T>>
{
typedef typename Concat<X<T>, typename Unravel<T>::type>::type type;
};
template <template <typename> class X>
struct Unravel<X<NullType>>
{
typedef std::tuple<X<NullType>> type;
};
template <typename T> struct printArgs;
template <typename T, typename ...Args>
struct printArgs<std::tuple<T, Args...>>
{
static void print() { std::cout << "Have type." << std::endl; printArgs<std::tuple<Args...>>::print(); }
};
template <typename T>
struct printArgs<std::tuple<T>>
{
static void print() { std::cout << "Have type." << std::endl; }
};
int main()
{
typedef A<B<C<NullType>>> CType;
printArgs<Unravel<CType>::type>::print();
}
It won't print anything exciting, so at the moment you just get one line per inheritance, but if you partially-specialize printArgs you can print specific information for your types.
This might work for you (if I understood the question correctly). I got it working with gcc, but think it should work even in VS2010.
void printTypeComponents(const Blank&)
{
std::cout << "composed of Blank\n";
}
template <typename T>
void printTypeComponents(const A<T>&)
{
std::cout << "composed of A\n";
printTypeComponents(T());
}
template <typename T>
void printTypeComponents(const B<T>&)
{
std::cout << "composed of B\n";
printTypeComponents(T());
}
template <typename T>
void printTypeComponents(const C<T>&)
{
std::cout << "composed of C\n";
printTypeComponents(T());
}
The advantage is that you do not need any typedefs inside the classes. If you want, you can put the logic inside printTypeComponentsImpl (or something like that) and have printTypeComponents delegate to that function.
You can avoid creating the temporaries, but since you cannot partially specialize functions, you'll have to move everything inside a struct and use that. If you want, I can put code example here.
edit: You could actually automate it a little bit with typeid(x).name(), provided you can extract the name of class template from it (named getTemplateName here).
template <template <typename> class T, typename U>
void printTypeComponents(const T<U>&)
{
std::cout
<< "composed of "
<< getTemplateName(typeid(T<DummyClass>).name())
<< '\n';
printTypeComponents(U());
}
For those interested, here's gcc specific example.