rationalcoder rationalcoder - 2 months ago 18
C++ Question

Meta-function Returning Type from Type List that Has a Typedef of a Given Type in C++11

I have run into a scenario in which I have a list of classes in a variadic template list, and, given a type (

Target_
), I wish to find the class in the list (
ContainingClass_
) that
typedef
s
Target_
as
ContainingClass_::Class
.

Here is my current, brute force implementation:

#include <iostream>
#include <cstdlib>
#include <type_traits>
#include <tuple>

template <uint32_t ID_, class Class_>
struct ContainingClass
{
using Class = Class_;
static constexpr uint32_t id() { return ID_; }
};

// Get the index of a type in a type list, or -1 on error (So that we only get the important static_assert).
template <typename Target_, typename ...List_>
struct IndexOf : std::integral_constant<int, -1> {};

template <typename Target_, typename NotTarget_, typename ...List_>
struct IndexOf<Target_, NotTarget_, List_...> : std::integral_constant<std::size_t, 1 + IndexOf<Target_, List_...>::value> {};

template <typename Target_, typename ...List_>
struct IndexOf<Target_, Target_, List_...> : std::integral_constant<std::size_t, 0> {};

// Check if a type exists in a typelist.
template <typename Target_, typename... List_>
struct TypeExists;

template <typename Target_>
struct TypeExists<Target_> : std::false_type {};

template <typename Target_, typename... List_>
struct TypeExists<Target_, Target_, List_...> : std::true_type {};

template <typename Target_, typename NotTarget_, typename... List_>
struct TypeExists<Target_, NotTarget_, List_...> : TypeExists<Target_, List_...> {};

// **THE META-FUNCTION THAT THE QUESTION IS ABOUT**
// Find the ContaingClass that typedefs Target_ as "Class" inside of it.
template <class Target_, class ...ContainingClasses_>
struct ContainingClassFinder
{
static_assert(TypeExists<Target_, typename ContainingClasses_::Class...>::value, "No ContainingClass found for Target_.");
using ContainingClass = typename std::tuple_element<IndexOf<Target_, typename ContainingClasses_::Class...>::value,
std::tuple<ContainingClasses_...>>::type;
};

using namespace std;

// Use the meta function to return the id of the ContainingClass that contains a type.
template <class Target_, class ...ContainingClasses_>
uint32_t get_containing_id(ContainingClasses_...)
{
return ContainingClassFinder<Target_, ContainingClasses_...>::ContainingClass::id();
}

struct Foo {};
struct Bar {};
struct Test {};
struct NonExistent {};

int main()
{
// Prove that the right class was found be printing its ID out.
// Prints 2.
cout << get_containing_id<Test>(ContainingClass<0, Foo>{}, ContainingClass<1, Bar>{}, ContainingClass<2, Test>{}) << endl;
// Causes static_assert.
//cout << get_containing_id<NonExistent>(ContainingClass<0, Foo>{}, ContainingClass<1, Bar>{}, ContainingClass<2, Test>{}) << endl;
return EXIT_SUCCESS;
}


The problem with this is that it depends on
std::tuple
and does two linear searches: one for the existence check (using the first helper meta-function) and one to get the type (using
std::tuple_element
).

I would Ideally like to get all of this in one go, with no need for the two helper meta-functions and
std::tuple
; is that practical? If not, any improvements to my implementation would be appreciated.

NOTE:

1. This meta-function will be used as an implementation detail of library; Boost is not an option.

2. The result of the meta-function should be a type, specifically that of the containing type in which the target type was found in. Getting the id() of the class is just a simple way to show that the meta-function is working.

3. By meta-function, I am referring to a templated struct, like the ones defined in
<type_traits>

Answer

What about the following solution?

No std::tuple and, if I'm not wrong, only one linear search.

#include <iostream>
#include <cstdlib>
#include <type_traits>

template <uint32_t, class Class_>
struct ContainingClass
 { using Class = Class_; };

struct Foo {};
struct Bar {};
struct Test {};

template <typename, typename ...>
struct getContType;

template <typename T>
struct getContType<T>
 { using type = void; };

template <typename T, typename CC0, typename ... CCs>
struct getContType<T, CC0, CCs...>
 { using type = typename std::conditional<std::is_same<T,
         typename CC0::Class>::value, CC0,
         typename getContType<T, CCs...>::type>::type; };

int main()
 {
   static_assert(std::is_same<ContainingClass<2, Test>,
                 getContType<Test, ContainingClass<0, Foo>,
                 ContainingClass<1, Bar>, ContainingClass<2, Test>>::type
                 >::value, "!");

   return EXIT_SUCCESS;
 }