I am trying to grasp the technique of recursive data-structure generation using TMP with this question.
Question
Suppose I have a variadic template template<typename... Ts> struct my_sets { };
.
In my_sets
I would like to generate a new type whose data members depends on Ts
.
For instance, I want my_sets
to have one std::set<T>
data member for every element/type that is in Ts...
using x_t = my_sets<int,char,std::string>;
x_t x;
x.insert<0>( 5 ); // into a std::set<int> member in my_sets<>
x.insert<1>( 'z' ); // into a std::set<char> member in my_sets<>
x.insert<2>( "foo" ); // into a std::set<std::string> member in my_sets<>
I think one way of accomplishing this may be through the use of subclassing and recursion, but I'm not sure.
fwiw, if it is more straight-forward to implement the mutator through a free function or plain function overloading, that's ok, too:
insert<0>( x, 5 ); // into a std::set<int> member in my_sets<>
insert<1>( x, 'z' ); // into a std::set<char> member in my_sets<>
insert<2>( x, "foo" ); // into a std::set<std::string> member in my_sets<>
What's wrong with std::tuple
here?
#include <tuple>
#include <set>
template<class... Ts>
using my_sets = std::tuple<std::set<Ts>...>;
// ...
auto x = my_sets<int, char, std::string>;
std::get<0>(x).insert(5);
std::get<1>(x).insert('z');
std::get<2>(x).insert("foo");
For looks, add a free insert
function:
#include <utility>
template<std::size_t I, class SetTuple, class Arg>
auto insert(SetTuple& st, Arg&& arg)
-> decltype(std::get<I>(st).insert(std::forward<Arg>(arg)))
{
return std::get<I>(st).insert(std::forward<Arg>(arg));
}
@Xeo has an elegant and simple solution. If you want to have insert<>
as a member function, however, you can use the following approach:
#include <set>
#include <tuple>
template<typename... Ts>
struct my_sets : protected std::set<Ts>...
{
using types = std::tuple<Ts...>;
template<int I, typename T>
typename std::pair<
typename std::set<typename std::tuple_element<I, types>::type>::iterator,
bool> insert(T&& t)
{
return std::set<typename std::tuple_element<I, types>::type>::insert(
std::forward<T>(t)
);
}
// ...
// Function for retrieving each set...
template<int I>
typename std::set<typename std::tuple_element<I, types>::type>& get()
{
return *this;
}
};
And this is how you would use it
#include <string>
int main()
{
my_sets<int, double, std::string> s;
s.insert<0>(42);
s.insert<1>(3.14);
s.insert<2>("Hello World!");
s.get<0>().insert(42);
}
Notice, that the above solution doesn't allow for multiple occurrences of the same type in the type list (which may or may not be desired), although it can be quite easily extended to allow them:
#include <set>
#include <tuple>
namespace detail
{
template<int... Is>
struct indices
{
typedef indices<Is..., sizeof...(Is)> next;
};
template<int I>
struct index_range
{
using type = typename index_range<I - 1>::type::next;
};
template<>
struct index_range<0>
{
using type = indices<>;
};
template<int I, typename T>
struct dummy : T { };
template<typename, typename... Ts>
struct my_sets { };
template<int... Is, typename... Ts>
struct my_sets<indices<Is...>, Ts...> : protected dummy<Is, std::set<Ts>>...
{
using types = std::tuple<Ts...>;
template<int I, typename T>
typename std::pair<
typename std::set<typename std::tuple_element<I, types>::type>::iterator,
bool
> insert(T&& t)
{
return dummy<I, std::set<typename std::tuple_element<I, types>::type>>::
insert(std::forward<T>(t));
}
template<int I>
dummy<I, std::set<typename std::tuple_element<I, types>::type>>& get()
{
return *this;
}
};
}
template<typename... Ts>
using my_sets = detail::my_sets<
typename detail::index_range<sizeof...(Ts)>::type,
Ts...
>;
And this is how you would use it:
#include <string>
int main()
{
my_sets<int, double, int, std::string> s;
s.insert<0>(42);
s.insert<1>(3.14);
s.insert<2>(1729);
s.insert<3>("Hello World!");
s.get<0>().insert(42);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With