samedi 24 septembre 2016

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 typedefs 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.

Aucun commentaire:

Enregistrer un commentaire