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

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.

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;
}

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;
}

Related

How to use variadic templates to proceed std::variant with std::visit?

I'm trying to write a logic, with usage of command and factory patterns. Based on a data inside the std::variant I want to create an object. It may look like a map of C++ types.
Here is the code: Wandbox
I can hardcode all used types but I want to automate it with variadic templates, how to do it?
#include <cassert>
#include <variant>
#include <type_traits>
#include <memory>
#include <iostream>
struct IFace
{
virtual void foo() = 0;
};
template<typename TKey, typename TValue>
struct Base : IFace
{
using Key = TKey;
using Value = TValue;
};
struct Int : Base<int, Int>
{
void foo() override { std::cout << "Int"; }
};
struct Double : Base<double, Double>
{
void foo() override { std::cout << "Double"; }
};
using Var = std::variant<int, double>;
template<typename ...Args>
std::shared_ptr<IFace> factory(const Var& v)
{
std::shared_ptr<IFace> p;
std::visit([&p](auto&& arg){
using TKey = std::decay_t<decltype(arg)>;
// TODO: use variadic instead of hardcoded types
if constexpr (std::is_same_v<TKey, int>)
{
using TValue = typename Base<TKey, Int>::Value;
p = std::make_shared<TValue>();
std::cout << "int ";
}
else if constexpr (std::is_same_v<TKey, double>)
{
using TValue = typename Base<TKey, Double>::Value;
p = std::make_shared<TValue>();
std::cout << "double ";
}
}, v);
return p;
}
int main()
{
const Var v = 42;
auto p = factory<Int, Double>(v);
assert(p != nullptr);
p->foo();
return 0;
}
This popular little "overloaded" class is always useful for passing lambdas to std::visit:
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
Then, instead of using a generic lambda and trying to deduce the right object type from the parameter type, just generate an overload for each Args.
template<typename ...Args>
std::shared_ptr<IFace> factory(const std::variant<typename Args::Key...>& v)
{
return std::visit(overloaded {
[](const typename Args::Key&) -> std::shared_ptr<IFace> { return std::make_shared<Args>(); }...
}, v);
}
Demo: https://godbolt.org/z/nKGf3c

How to create a vector of tuples in C++?

There is a generator which generates a tuple of 16 integers with each iteration. I want to add these tuples into a vector. While creating the vector I have to write std::vector<std::tuple<int, int, .... 16 times>>. Is there another way to create a vector of these tuples.
Code to test for the case of tuples containing 5 integers:
#include "cppitertools/itertools.hpp"
#include <iostream>
#include <tuple>
#include <vector>
int main()
{
std::vector<int> v1{0, 1};
std::vector<std::tuple<int, int, int, int, int>> states;
for (auto&& i : iter::product<5>(v1))
{
states.push_back(i);
}
auto size = states.size();
std::cout << size << std::endl;
}
I'm using cppiterator
template<size_t Remaining, typename Type,typename ... Args>
struct make_tuple_n_impl{
using type=typename make_tuple_n_impl<Remaining-1,Type,Type,Args...>::type;
};
template<typename T,typename ... Args>
struct make_tuple_n_impl<0,T,Args...>{
using type=std::tuple<Args...>;
};
template<size_t Count,typename Type>
using tuple_of=typename make_tuple_n_impl<Count,Type>::type;
Then tuple_of<5,int> is std::tuple<int,int,int,int,int>.
You can just fiddle the tuple type that iter::product creates
#include "cppitertools/itertools.hpp"
#include <iostream>
#include <tuple>
#include <vector>
template <typename>
struct remove_tuple_reference;
template <typename... Ts>
struct remove_tuple_reference<std::tuple<Ts...>> {
using type = std::tuple<std::remove_reference_t<Ts>...>;
};
template <typename T>
using remove_tuple_reference_t = typename remove_tuple_reference<T>::type;
int main()
{
std::vector<int> v1{0, 1};
using tup = remove_tuple_reference_t<decltype(iter::product<5>(v1).begin())::value_type>;
std::vector<tup> states;
for (auto&& i : iter::product<5>(v1))
{
states.push_back(i);
}
auto size = states.size();
std::cout << size << std::endl;
}

How to "iterate" over a list of templates at compile time?

This is the extraction of a follow-up question to this answer.
Given the following "loop" technique
#pragma once
// loop.hpp
#include <type_traits>
#include <utility>
template<std::size_t... indices, class LoopBody>
void loop_impl(std::index_sequence<indices...>, LoopBody&& loop_body) {
(// C++17's fold expression
loop_body(std::integral_constant<std::size_t, indices>{}),
...
);
}
template<std::size_t N, class LoopBody>
void loop(std::integral_constant<std::size_t, N>, LoopBody&& loop_body) {
loop_impl(std::make_index_sequence<N>{}, std::forward<LoopBody>(loop_body));
}
it is possible to iterate over a type list like this:
#include <iostream>
#include <string_view>
#include <tuple>
#include "loop.hpp"
template<class T>
std::string_view inspect() {
return __PRETTY_FUNCTION__;
}
using Types = std::tuple<int, int, char, bool, double>;
int main() {
loop(std::tuple_size<Types>{}, [&] (auto i) {
using T = std::tuple_element_t<i, Types>;
std::cout << i << ": " << inspect<T>() << "\n";
});
}
... but how to iterate over a list of templates?
Using Boost.Mp11, the first version is:
static constexpr auto size = std::tuple_size_v<Types>;
mp_for_each<mp_iota_c<size>>([&] (auto i) {
/* ... */
});
Doing this over templates works basically the same way:
using list = mp_list<mp_quote<std::tuple>, mp_quote<std::pair>>;
mp_for_each<list>([&](auto f){
// v is a tuple<int, char> the first time around and a pair<int, char>
// the second time around
mp_invoke_q<decltype(f), int, char> v;
});
Of course you can do whatever you want with f in the body, I just invoked it as an example of mp_quote and how well integrated it is with the rest of Mp11.
You may wrap templates of the form template<class...> class into a tag type like the following:
#pragma once
// template_tag.hpp
#include <tuple>
#include <type_traits>
template<
template<class...>
class Tmpl_
>
struct TemplateTag {
template<class... Ts>
using insert = Tmpl_<Ts...>;
template<
template<template<class... > class>
class TmplTmpl
>
using rewrap_into = TmplTmpl<Tmpl_>;
};
// convenience helper
template<class TmplTag, class... Ts>
using InsertTemplateArgs = typename TmplTag::template insert<Ts...>;
static_assert(
std::is_same<
InsertTemplateArgs< TemplateTag<std::tuple>, int, bool >,
std::tuple<int, bool>
>{}
);
// convenience helper
template<class TmplTag, template<template<class...> class> class TmplTmpl>
using RewrapTemplateInto = typename TmplTag::template rewrap_into<TmplTmpl>;
template<template<class...> class Tmpl>
struct OtherTemplateTag {};
static_assert(
std::is_same<
RewrapTemplateInto< TemplateTag<std::tuple>, OtherTemplateTag >,
OtherTemplateTag<std::tuple>
>{}
);
Once your templates are wrapped into a tag type, you can iterate over the types as before:
#include <iostream>
#include <string_view>
#include <tuple>
#include <utility>
#include <variant>
#include "loop.hpp"
#include "template_tag.hpp"
template<class T>
std::string_view inspect() {
return __PRETTY_FUNCTION__;
}
using Templates = std::tuple<
TemplateTag<std::tuple>,
TemplateTag<std::tuple>,
TemplateTag<std::pair>,
TemplateTag<std::variant>
>;
template<
template<class...>
class Tmpl
>
struct AnotherTemplateTag {};
int main() {
loop(std::tuple_size<Templates>{}, [&] (auto i) {
using TmplTag = std::tuple_element_t<i, Templates>;
std::cout << i << ": " << inspect<TmplTag>() << "\n";
using AnotherTmplTag = RewrapTemplateInto<TmplTag, AnotherTemplateTag>;
std::cout << " " << inspect<AnotherTmplTag>() << "\n";
using TmplWithArgs = InsertTemplateArgs<TmplTag, int, long>;
std::cout << " " << inspect<TmplWithArgs>() << "\n";
});
}

Why is the constructor in this C++ code ambiguous and how do I fix it?

In the below code, the compiler can't figure out which constructor I want to use. Why, and how do I fix this? (Live example)
#include <tuple>
#include <functional>
#include <iostream>
template<typename data_type, typename eval_type, typename Type1, typename Type2>
class A
{
public:
using a_type = std::tuple<Type1, Type2>;
using b_type = std::tuple<std::size_t,std::size_t>;
inline explicit constexpr A(const std::function<data_type(a_type)>& Initializer,
const std::function<eval_type(data_type)>& Evaluator,
const Type1& elem1, const Type2& elem2)
{
std::cout << "idx_type" << std::endl;
}
inline explicit constexpr A(const std::function<data_type(b_type)>& Initializer,
const std::function<eval_type(data_type)>& Evaluator,
const Type1& elem1, const Type2& elem2)
{
std::cout << "point_type" << std::endl;
}
};
int main()
{
int a = 1;
long long b = 2;
auto c = A<double, double, long long, int>{
[](std::tuple<long long,int> p)->double { return 1.0*std::get<0>(p) / std::get<1>(p); },
[](double d)->double { return d; }, b,a
};
return 0;
}
The reason it doesn't work is because a lambda is not a std::function and so the compiler tries to create one using the fifth overload of the constructor. The problem is that both of your A constructors can be used because of this conversion and the reason that std::tuple<long long,int> and std::tuple<std::size_t,std::size_t> are constructible from each other makes this ambigious for the compiler what constructor to pick.
What you could do is explicitly cast to the desired std::function (MCVE of #PasserBy used in comments), like this:
#include <tuple>
#include <functional>
#include <iostream>
template<typename data_type, typename Type1, typename Type2>
class A
{
public:
using a_type = std::tuple<Type1, Type2>;
using b_type = std::tuple<std::size_t,std::size_t>;
A(const std::function<data_type(a_type)>&)
{
std::cout << "idx_type" << std::endl;
}
A(const std::function<data_type(b_type)>&)
{
std::cout << "point_type" << std::endl;
}
};
int main()
{
std::function<double(std::tuple<long long, int>)> func = [](auto p) -> double { return 1; };
auto c = A<double, long long, int>{
func
};
}
As #SombreroChicken mentioned, std::function<R(Args...)> has a constructor that allows any callable object c to initialize it, as long as c(Args...) is valid and returns something convertible to R.
To fix it, you may use some SFINAE machinery
#include <tuple>
#include <functional>
#include <iostream>
#include <type_traits>
template<typename data_type, typename Type1, typename Type2>
class A
{
template<typename T>
struct tag
{
operator T();
};
public:
using a_type = std::tuple<Type1, Type2>;
using b_type = std::tuple<std::size_t,std::size_t>;
template<typename C, std::enable_if_t<std::is_invocable_v<C, tag<b_type>>>* = nullptr>
A(C&& initializer)
{
std::cout << "size_t" << std::endl;
}
template<typename C, std::enable_if_t<std::is_invocable_v<C, tag<a_type>>>* = nullptr>
A(C&& initializer)
{
std::cout << "other" << std::endl;
}
};
int main()
{
auto c = A<double, long long, int>{
[](std::tuple<long long, int> p) -> double { return 1; }
};
auto c2 = A<double, long long, int>{
[](std::tuple<std::size_t, std::size_t>) -> double { return 2; }
};
}
Live
Here, we turn off the constructor if the callable can be called with b_type or a_type respectively. The extra indirection through tag is there to disable the conversion between tuples of different types

boost::hana::map as a data member

I am currently learning boost::hana for a personal project.
In the following code snippet I created a boost::hana::map having a type_c<T> as key and an instance of Foo<T> as value.
It works but I would really like to use the my_map variable as a class member and it is not possible to use auto qualifier in member declarations. Moreover it would be great if I could have the possibility to pass the types tuple in some way (as a template parameter or as a constructor parameter).
Do you have any suggestions?
#include <iostream>
#include "boost/hana.hpp"
#include <typeinfo>
using namespace boost;
template<typename T>
class Foo {
T t;
public:
void print() { std::cout <<typeid(T).name() << t; }
};
int main() {
auto types = hana::tuple_t<float, int, std::string>;
auto my_map = boost::hana::unpack(types, [](auto ...t) {
return boost::hana::make_map(boost::hana::make_pair(t, Foo<typename decltype(t)::type>()) ...);
});
my_map[hana::type_c<int>].print();
}
The problem is that lambdas cannot be used in un-evaluated context (decltype).
Of course, since c++14 we can just use deduced return types on any free function:
namespace detail {
template <typename... T>
static inline auto make_foo_map() {
return boost::hana::unpack(hana::tuple_t<T...>, [](auto... t) {
return boost::hana::make_map(boost::hana::make_pair(t, Foo<typename decltype(t)::type>())...);
});
}
}
template <typename... T>
using FooMap = decltype(detail::make_foo_map<T...>());
Now it's simply:
FooMap<float, int, std::string> my_map;
Live Demo
Live On Coliru
#include "boost/hana.hpp"
#include <iostream>
#include <typeinfo>
using namespace boost;
template <typename T> class Foo {
T t;
public:
void print() { std::cout << typeid(T).name() << "t\n"; }
};
namespace detail {
template <typename... T>
static inline auto make_foo_map() {
return boost::hana::unpack(hana::tuple_t<T...>, [](auto... t) {
return boost::hana::make_map(boost::hana::make_pair(t, Foo<typename decltype(t)::type>())...);
});
}
}
template <typename... T>
using FooMap = decltype(detail::make_foo_map<T...>());
int main() {
FooMap<float, int, std::string> my_map;
my_map[hana::type_c<int>].print();
my_map[hana::type_c<float>].print();
my_map[hana::type_c<std::string>].print();
}