I am attempting to convert between two types of std::tuples but I'm having trouble getting the implementation right. I want it to map if the type is the same, but it needs to allow duplicate types to get mapped in the original order.
The basic logic is:
while has pair<input, output>
if type(input) == type(output)
then do map, next input, next output
else
next input
assert(all outputs mapped)
With an example mapping being:

My "best" attempt at a solution looks like this:
template <auto> struct value {};
template <auto... Vals> struct value_sequence {};
template <class... Vals> struct placeholder {};
template <auto... As, auto... Bs>
constexpr value_sequence<As..., Bs...> operator+(value_sequence<As...>, value_sequence<Bs...>)
{
return {};
}
template <size_t Idx, size_t... Idxs, size_t OtherIdx, size_t... OtherIdxs, class T, class... Ts, class OtherT, class... OtherTs>
constexpr auto mapper(const std::index_sequence<Idx, Idxs...>&, const std::index_sequence<OtherIdx, OtherIdxs...>&, const placeholder<T, Ts...>&,
const placeholder<OtherT, OtherTs...>&)
{
if constexpr (sizeof...(OtherIdxs) == 0)
{
static_assert(std::is_same_v<T, OtherT>);
return value_sequence<Idx>{};
}
else if constexpr (std::is_same_v<T, OtherT>)
{
return value_sequence<Idx>{} +
mapper(std::index_sequence<Idxs...>{}, std::index_sequence<OtherIdxs...>{}, placeholder<Ts...>{}, placeholder<OtherTs...>{});
}
else
{
return mapper(std::index_sequence<Idx, Idxs...>{}, std::index_sequence<OtherIdxs...>{}, placeholder<T, Ts...>{}, placeholder<OtherTs...>{});
}
}
Called with:
mapper(std::make_index_sequence<sizeof...(Ts)>{}, std::make_index_sequence<sizeof...(OtherTs)>{},
placeholder<Ts...>{}, placeholder<OtherTs...>{})
Which gives the compiler error error C2672: 'mapper': no matching overloaded function found pointing to the else if case of the function mapper
I'm working in c++17, and it looks like all the bits I need are there, I just can't assemble them the right way.
Any help would be really appreciated!
Here's a fairly simple recursive implementation:
// pop_front implementation taken from https://stackoverflow.com/a/39101723/4151599
template <typename Tuple, std::size_t... Is>
auto pop_front_impl(const Tuple& tuple, std::index_sequence<Is...>)
{
return std::make_tuple(std::get<1 + Is>(tuple)...);
}
template <typename Tuple>
auto pop_front(const Tuple& tuple)
{
return pop_front_impl(tuple,
std::make_index_sequence<std::tuple_size<Tuple>::value - 1>());
}
template <typename...>
std::tuple<> map_tuple(std::tuple<>)
{
return {};
}
template <typename FirstOutput, typename... Outputs,
typename FirstInput, typename... Inputs>
std::tuple<FirstOutput, Outputs...>
map_tuple(const std::tuple<FirstInput, Inputs...>& input)
{
if constexpr (std::is_same_v<FirstInput, FirstOutput>) {
return std::tuple_cat(
std::tuple<FirstOutput>(std::get<0>(input)),
map_tuple<Outputs...>(pop_front(input))
);
} else {
return map_tuple<FirstOutput, Outputs...>(pop_front(input));
}
}
Simply call it like
std::tuple<int, double, char, int, int, float> tup = {1, 2.0, '3', 4, 5, 6.0};
auto mapped = map_tuple<int, double, char, float>(tup);
Demo
To begin, need a helper template to peel off the first type from a tuple.
#include <utility>
#include <type_traits>
#include <tuple>
#include <cstdlib>
#include <iostream>
template<typename T>
struct peel_tuple_t;
template<typename T, typename ...Args>
struct peel_tuple_t< std::tuple<T, Args...>> {
typedef std::tuple<Args...> type_t;
};
template<typename T>
using peel_tuple=typename peel_tuple_t<T>::type_t;
static_assert(std::is_same_v< peel_tuple<std::tuple<int, float>>,
std::tuple<float>>);
That's simple enough: std::tuple<int, float> -> std::tuple<float>.
Next, we need an equivalent of tuple_cat, but for index_sequences:
template<typename T, typename T2> struct index_sequence_cat_t;
template<size_t ...A, size_t ...B>
struct index_sequence_cat_t<std::index_sequence<A...>,
std::index_sequence<B...>> {
typedef std::index_sequence<A..., B...> type_t;
};
template<typename T, typename T2>
using index_sequence_cat=typename index_sequence_cat_t<T, T2>::type_t;
static_assert(std::is_same_v<index_sequence_cat<
std::index_sequence<1, 2>,
std::index_sequence<3, 4>
>, std::index_sequence<1, 2, 3, 4>>);
We can now use this to take a std::tuple<float, int, double, double>>, which provides the values, then a std::tuple<float, double, double>, where the values go, and produce a std::index_sequence<0, 2, 3> that will tell us to std::get index 0, 2, and 3 from the source tuple into the destination tuple:
template<typename output_tuple, typename input_tuple, size_t index>
struct map_input_tuple_t : map_input_tuple_t<output_tuple,
peel_tuple<input_tuple>,
index+1> {};
template<typename T, typename ...Args2, size_t index>
struct map_input_tuple_t<std::tuple<T>,
std::tuple<T, Args2...>, index> {
typedef std::index_sequence<index> type_t;
};
template<typename T, typename ...Args, typename ...Args2, size_t index>
struct map_input_tuple_t<std::tuple<T, Args...>,
std::tuple<T, Args2...>, index> {
typedef index_sequence_cat<
std::index_sequence<index>,
typename map_input_tuple_t<std::tuple<Args...>,
std::tuple<Args2...>,
index+1>::type_t> type_t;
};
template<typename output_tuple, typename input_tuple>
using map_input_tuple=
typename map_input_tuple_t<output_tuple, input_tuple, 0>::type_t;
static_assert(std::is_same_v< map_input_tuple<
std::tuple<float, double, double>,
std::tuple<float, int, double, double>>,
std::index_sequence<0, 2, 3>>);
And now, this is a solved problem:
template<typename mapping> struct map_tuple_impl;
template<size_t ...n>
struct map_tuple_impl<std::index_sequence<n...>> {
template<typename T>
static auto doit(const T &t)
{
return std::tuple{ std::get<n>(t)... };
}
};
template<typename output_tuple, typename ...input_types>
auto map_tuple(const std::tuple<input_types...> &input)
{
return map_tuple_impl<map_input_tuple<output_tuple,
std::tuple<input_types...>>>
::doit(input);
}
int main()
{
auto t=map_tuple<std::tuple<float, double, double>
>(std::tuple{1.0f, 2,
2.0,
3.0});
static_assert(std::is_same_v<decltype(t),
std::tuple<float, double, double>>);
if (t == std::tuple{1.0F, 2.0, 3.0})
std::cout << "yes\n";
return 0;
}
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