Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ recursively nested types and name injection

I tried googling this with no luck, so I'm trying here.

I have a couple of classes, each of which defines a member struct foo. This member type foo can itself inherit from one of the former classes, hence obtaining a member type foo itself.

I want to access the nested foo types using template metaprogramming (see below), but c++ name injection introduces problems, as the upper foo type name gets injected into the lower foo type, and the upper one gets resolved when I want to access the lower, say using A::foo::foo.

Here is an example:

#include <type_traits>

struct A;
struct B;

struct A {
    struct foo;
};

struct B {
    struct foo;
};

struct A::foo : B { };
struct B::foo : A { };

// handy c++11 shorthand
template<class T>
using foo = typename T::foo;

static_assert( std::is_same< foo< foo< A > >, foo< B > >::value, 
               "this should not fail (but it does)" );

static_assert( std::is_same< foo< foo< A > >, foo< A > >::value, 
               "this should fail (but it does not)" );

FYI, I'm implementing function derivatives, foo is the derivative type. The above situation happens e.g. with sin/cos.

TLDR: how do i get foo<foo<A>> to be foo<B>, not foo<A> ?

Thanks !

like image 821
max Avatar asked Oct 17 '12 15:10

max


1 Answers

This is not really an automatic solution but solves the problem. Your types provide a typedef to the base class, absence/presence of this typedef is detected through SFINAE and the nested foo is found either through the base or through normal look-up.

You can probably automate the has_base to check a list of known bases with is_base_of if you need more automation.

#include <type_traits>
template <typename T>
struct has_base
{
    typedef char yes[1];
    typedef char no[2];

    template <typename C>
    static yes& test(typename C::base*);

    template <typename>
    static no& test(...);

    static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};

struct A {
    struct foo;
};

struct B {
    struct foo;
};

struct A::foo : B { typedef B base; };
struct B::foo : A { typedef A base; };

template<typename T, bool from_base = has_base<T>::value >
struct foo_impl {
  typedef typename T::base::foo type;
};

template<typename T> 
struct foo_impl<T, false> {
  typedef typename T::foo type;
};

template<typename T>
using foo = typename foo_impl<T>::type;

static_assert( std::is_same< foo< foo<A> >::, foo< B > >::value, 
               "this should not fail (but it does)" );

static_assert( std::is_same< foo< foo< A > >, foo< A > >::value, 
               "this should fail (but it does not)" );
int main()
{

  return 0;
}
like image 89
pmr Avatar answered Oct 18 '22 11:10

pmr