6

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.

Community
  • 1
  • 1
Luis Machuca
  • 1,047
  • 9
  • 16
  • 1
    What is the problem that this is intended to solve? – Pete Becker May 01 '13 at 18:51
  • I'm mostly theorizing on how to improve my [adaption of typesafe enums in C++03](http://stackoverflow.com/a/11856721/399580) by disabling relational operators for `enum`s which are nested inside structs; this is one of two approaches I'm studying to tackle that problem. – Luis Machuca May 01 '13 at 18:57

2 Answers2

1

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.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • Excellent point on the matter of pointer-as-iterator and other similar situations that could arise. At that point though I'd expect the compiler to be able to solve it as the iterator _type_ is known; in such case, this trait would for example help determine if a container type has contiguous storage (would definitively yield `false` if the iterator type is a raw pointer, and _likely_ yield `true` otherwise). – Luis Machuca May 01 '13 at 19:02
  • @LuisMachuca: Not sure what you mean when you say "to solve *it*". What do you mean by "it"? Your `is_nested<>` trait would get a *type* in input, which in both cases is `int*`. That's everything `is_nested<>` knows. There is no way you can program `is_nested<>` to produce different results given the same template arguments. – Andy Prowl May 01 '13 at 19:05
  • …True, unfortunately. This means I'll have to discard this approach. – Luis Machuca May 01 '13 at 19:27
  • @LuisMachuca: I'm afraid so :( – Andy Prowl May 01 '13 at 19:30
  • Um, at this point I should ask what is the correct protocol here? Is the question marked as answered-but-not-solved, closed overall, or...? – Luis Machuca May 05 '13 at 22:51
1

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.

jingyu9575
  • 521
  • 3
  • 16