Suppose that we have two tuples a
and b
.
I want to implement a function merge_tuples
that creates a new tuple c
that is a merge of a
and b
according to a provided binary mask: if a bit is set, we take one item of a
, otherwise we take one item of b
.
Note that the returned tuple should reference the items of a
and b
(i.e. we don't want to copy objects here).
#include <tuple>
#include <cassert>
template<unsigned long long MASK, typename...A, typename...B>
auto merge_tuples (std::tuple<A...>& a, std::tuple<B...>& b)
{
// In the example below, we kwow that we have to return the following:
return std::tie
(
std::get<0> (a),
std::get<0> (b),
std::get<1> (a),
std::get<1> (b),
std::get<2> (b)
);
// BUT HOW TO DO IT GENERICALLY BY USING 'MASK' ?
}
int main ()
{
std::tuple<float,int> a {float {3.1}, int{2} };
std::tuple<double,int,char> b {double{1.123}, int{4}, char{9} };
auto c = merge_tuples <0b00101> (a,b);
assert (std::get<0>(c) == float {3.1} ); // mask[0]==1 => take item 0 from a
assert (std::get<1>(c) == double{1.123}); // mask[1]==0 => take item 0 from b
assert (std::get<2>(c) == int {2} ); // mask[2]==1 => take item 1 from a
assert (std::get<3>(c) == int {4} ); // mask[3]==0 => take item 1 from b
assert (std::get<4>(c) == char {9} ); // mask[4]==0 => take item 2 from b
}
How can such a merge_tuples
function be implemented?
std::index_sequence
might help:
template <std::size_t N>
constexpr std::array<std::pair<std::size_t, std::size_t>, N>
mask_to_array(std::uint64_t mask)
{
std::array<std::pair<std::size_t, std::size_t>, N> res{};
std::size_t ai = 0;
std::size_t bi = 0;
for (auto& [ab, i] : res) {
ab = mask & 1;
i = ab ? ai++ : bi++;
mask >>= 1;
}
return res;
}
template <std::uint64_t MASK, typename...A, typename...B>
constexpr auto merge_tuples (std::tuple<A...>& a, std::tuple<B...>& b)
{
constexpr std::size_t N = sizeof...(A) + sizeof...(B);
constexpr auto arr = mask_to_array<N>(MASK);
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return std::tie(std::get<arr[Is].second>(std::get<arr[Is].first>(std::tie(b, a)))...);
}(std::make_index_sequence<N>());
}