Haggai Magen
Haggai Magen

Reputation: 103

Instantiation of a variadic template class when given an std::tuple<T...>?

I am trying to write a handler class that when given a tuple, it can dynamically handle a specific field from the given tuple.

The problem is, I don't know how to create an instance of that class because the class is templated, and the template I need for the instantiation is inside the tuple.

(It is important to have the handler in a separate class due to design requests which are irrelevant to the question)

Notice the ??? in the ILevelHandler instance, I am required to supply the template but I'm not sure how to do it.

#include <tuple>
#include <string>
#include <iostream>
#include <boost/variant.hpp>

template <typename... T>
class ILevelHandler
{
public:
    virtual void HandleEnterLevel(const boost::variant<T...>& _value)
    {
        std::cout << " value: " << _value << std::endl;
    }   
};

int main()
{
    std::tuple<int, float, std::string, int> tpl {4, 6.6, "hello", 7};
    ILevelHandler<???> lvl(tpl);
    for (size_t i = 0; i < 4; ++ i)
    {
        lvl.HandleEnterLevel(i, dynamic_get(i, tpl));
    }
    return 0;
}

Important to mention: solving the problem with a function that is not wrapped inside a class is easy, although, I need to supply an abstract class, so that the user will have to implement the function on his own.

Upvotes: 2

Views: 186

Answers (2)

Richard Hodges
Richard Hodges

Reputation: 69864

assuming you have already written dynamic_get (say if you need help with that) then a simple conversion class should do it:

#include <tuple>
#include <string>
#include <iostream>
#include <boost/variant.hpp>

template <typename... T>
class ILevelHandler
{
public:
    void HandleEnterLevel(const boost::variant<T...>& _value)
    {
        std::cout << " value: " << _value << std::endl;
    }
};

template<class Thing>
struct to_variant;

template<class...T>
struct to_variant<std::tuple<T...>>
{
    using type = boost::variant<T...>;
};

template<class T> using to_variant_t = typename to_variant<T>::type;


int main()
{
    std::tuple<int, float, std::string, int> tpl {4, 6.6, "hello", 7};
    using tuple_type = decltype(tpl);
    using variant_type = to_variant_t<tuple_type>;
    ILevelHandler< variant_type > lvl;
    for (size_t i = 0; i < 4; ++ i)
    {
        lvl.HandleEnterLevel(i, dynamic_get(i, tpl));
    }
    return 0;
}

Upvotes: 2

skypjack
skypjack

Reputation: 50540

As a possible solution, you can use a support fake function as it follows:

#include <tuple>
#include <string>

template <typename... T>
class ILevelHandler {
public:
    ILevelHandler(const std::tuple<T...> &) {}
};

template<typename... T>
auto f(const std::tuple<T...> &) -> ILevelHandler<T...>;

int main() {
    std::tuple<int, float, std::string, int> tpl {4, 6.6, "hello", 7};
    decltype(f(tpl)) lvl(tpl);
    return 0;
}

Note that you don't need to define the function f, a simple declaration like the one in the example above is enough.
You can use also a couple of using declarations to clean up a bit your statements:

// ...

template<typename T>
using MyILevelHandler = decltype(f(std::declval<T>()));

// ...

int main() {
    using MyTuple = std::tuple<int, float, std::string, int>;

    MyTuple tpl {4, 6.6, "hello", 7};
    MyILevelHandler<MyTuple> lvl(tpl);
    return 0;
}

Upvotes: 3

Related Questions