5

Suppose we have the following template class

template<typename T> class Wrap { /* ... */ };

We can not change Wrap. It is important.

Let there are classes derived from Wrap<T>. For example,

class NewInt  : public Wrap<int>     { /* ... */ };
class MyClass : public Wrap<myclass> { /* ... */ };
class Foo     : public Wrap<Bar>     { /* ... */ };

We can not change these classes too. All classes above is 3rd party. They are not mine.

I need the following compile time type_traits:

template<class T>
struct is_derived_from_Wrap { 
     static const bool value = /* */;
};

What do I need?

assert(is_derived_from_Wrap<Int>::value == true);  // Indeed I need static assert
assert(is_derived_from_Wrap<MyClass>::value == true);
assert(is_derived_from_Wrap<char>::value == false);
struct X {};
assert(is_derived_from_Wrap<X>::value == false);
Alexey Malistov
  • 26,407
  • 13
  • 68
  • 88

3 Answers3

9

You can do this using SFINAE but its kind of magical if you dont know whats going on...

template<typename T> class Wrap { };

struct myclass {};
struct X {};

class Int     : public Wrap<int>     { /* ... */ };
class MyClass : public Wrap<myclass> { /* ... */ };

template< typename X >
struct is_derived_from_Wrap
{
  struct true_type { char _[1]; };
  struct false_type { char _[2]; };

  template< typename U >
    static true_type test_sfinae( Wrap<U> * w);
  static false_type test_sfinae( ... );

  enum { value = sizeof( test_sfinae( (X*)(0) ) )==sizeof(true_type) };
};


#include <iostream>
#define test(X,Y) std::cout<<( #X " == " #Y )<<"  : "<<( (X)?"true":"false") <<std::endl;

int main()
{
  test(is_derived_from_Wrap <Int>::value, true);
  test(is_derived_from_Wrap <MyClass>::value, true);
  test(is_derived_from_Wrap <char>::value, false);
  test(is_derived_from_Wrap <X>::value, false);
}

This gives the expected output

is_derived_from_Wrap <Int>::value == true  : true
is_derived_from_Wrap <MyClass>::value == true  : true
is_derived_from_Wrap <char>::value == false  : false
is_derived_from_Wrap <X>::value == false  : false

There are a couple of gotchas with my code. It will also return true if the type is a Wrap.

assert(  is_derived_from_Wrap< Wrap<char> >::value == 1 );

This can probably be fixed using a bit more SFINAE magic if needed.

It will return false if the derivation is not a public derivation (i.e is private or protected )

struct Evil : private Wrap<T> { };
assert( is_derived_from_Wrap<Evil>::value == 0 );

I suspect this can't be fixed. (But I may be wrong ). But I suspect public inheritance is enough.

Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
0

The following determines whether something is a wrap:

template<class T>
struct is_Wrap { static const bool value = false; };

template<typename T> 
struct is_Wrap< Wrap<T> > { static const bool value = true; };

Since derivation is an Is-A relationship, everything derived from Wrap<T> also is a Wrap<T> and should be found by this.

sbi
  • 219,715
  • 46
  • 258
  • 445
  • My name `base of Wrap` is wrong. My bad english. `Derived from wrap`, of course. – Alexey Malistov Jan 14 '10 at 08:57
  • It's not very good solution. Then I need to make DEFINE for the each class. I do not know all of them. I want to recognize whether X derived from Wrap where Y can be any type. – Alexey Malistov Jan 14 '10 at 09:05
  • @Alexey: I'll try to adapt my code accordingly. But you definitely need to be more precise in asking your questions. (And that's not just a language barrier problem.) – sbi Jan 14 '10 at 09:19
  • This only tests if the type is exactly a Wrap, rather than derived from. – Michael Anderson Jan 14 '10 at 09:51
  • @Michael; But isn't anythign derive from `Wrap` also a `Wrap`? – sbi Jan 14 '10 at 14:29
0

You need to do some fairly involved template metaprogramming to determine if one class X is derived from another Y, in the general case. Basically X derives from Y if:

  1. Y can be implicitly converted to X
  2. X and Y are not the same type

Andrei Alexandrescu explains how to do this (along with many other template tricks) in his book "Modern C++ Design".

You can find code which solves your problem either in the Loki library, or the uSTL implementation, both written by Alexandrescu.

Gareth Stockwell
  • 3,112
  • 18
  • 23
  • 1
    Having re-read the question, I realise that my suggestion doesn't solve the problem. We don't want to know whether X derived from Y, but whether X derives from Y, where T can be any type. – Gareth Stockwell Jan 14 '10 at 08:47
  • Yes. Your answer is not solution. But you have realized the problem. – Alexey Malistov Jan 14 '10 at 09:10
  • You could also cite boost::is_base_of (which doesn't address the OP's issue since the Base parameter cannot be a class template). – Luc Touraille Jan 14 '10 at 10:40