Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ - Know if a type / class is nested?

After seeing many examples of metaprogramming in C++ that allow for figuring out may properties of classes (such as knowing if a type is a specialization of a template ), or knowing if a class incorporates a given nested type; but I was wondering if is was possible to write a test or trait that determines the inverse of the last one - to check if a given Type is nested within a class or struct.

In other words, I'm looking for the equivalent of the following pseudocode:

template <typename Type> struct is_nested {
    enum { value = {__some magic__} };
};

typedef int type1;
struct Something { typedef int internal_type; };
typedef Something::internal_type type2;

//...later, likely at a different scope

is_nested< int >::value; // yields false
is_nested< std::vector<int>::iterator >::value; // yields true
is_nested< type1 >::value; // yields false
is_nested< type2 >::value; // yields true

I know I can use sizeof to implement yes/no tests, and I presume Type is part of those tests, but I can't figure out how to plug in some sort of "any viable type" into the test such that I can form an expression like Anytype::Type.

template 
struct is_nested
{
    typedef char yes;
    typedef struct { char u[2]; } no;

    // Herein lies the problem
    ???? static yes test( char [ sizeof(Anytype::Type) ] ) ;
    ???? static no test(...);


public:
    enum { value = sizeof(test(0)) == sizeof(char) };
};

(Note that I don't care nor (can afford to) know what type would Type be nested in; all it matters is if it is nested in something or not. In other words, this trait should only depend on Type.)

I'm looking for a C++ solution be it in C++11 or C++03, but in the first case I would welcome it much more if it was backportable.

like image 248
Luis Machuca Avatar asked May 01 '13 18:05

Luis Machuca


2 Answers

What you are asking is not possible, but not due to a technical limitation, rather because you can't always tell whether a type name identifies a nested type or not - and templates work with types, not names.

In this case, for instance:

is_nested< std::vector<int>::iterator >::value

You do not know what iterator is. Consider this class my_vector:

template<typename T>
struct my_vector
{
    typedef T* iterator;
    // ...
};

What should is_nested<my_vector<int>::iterator>::value yield? You probably expect the result to be true.

However, what is nested here is the alias, not the type itself: the type int* is not nested. In fact, I expect you would wish the following to yield false:

is_nested<int*>::value

So here the same is_nested<T> trait should yield two different results given the same type T (int*, in this case). The information based on which is_nested<> should define value cannot be retrieved from the type T itself - and templates work with types, not names.

like image 71
Andy Prowl Avatar answered Oct 17 '22 00:10

Andy Prowl


It may be possible to check if the "canonical type" (the result type after all aliases are resolved) is nested using non-standard features of compilers.

CTTI can get the name of a type at compile time. Then find : in the string:

#include <vector>
#include "ctti/type_id.hpp"

constexpr bool has_colon(const ctti::detail::string& s, size_t i) {
    return i < s.length() ? (s[i] == ':' || has_colon(s, i + 1)) : false;
}

template<typename T>
using is_nested = integral_constant<bool, has_colon(ctti::type_id<T>().name(), 0)>;

typedef int type1;
struct Something { typedef int internal_type; };
typedef Something::internal_type type2;

static_assert(!is_nested< int >::value, "");
static_assert(is_nested< std::vector<int>::iterator >::value, "");
static_assert(!is_nested< type1 >::value, "");
// static_assert(is_nested< type2 >::value, ""); // fail

The 4th check will fail because type2 is just int, which is not nested.

like image 32
jingyu9575 Avatar answered Oct 16 '22 22:10

jingyu9575