Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transform tuple type to another tuple type

Suppose I've got a tuple type std::tuple<x,y,z>, or maybe std::tuple<a,b>. I'd like a general purpose way to transform the types in my tuple, e.g. "functionize" to get

std::tuple<std::function<void(x)>,
           std::function<void(y)>,
           std::function<void(z)>>

or maybe get storage for shared_pointers like

std::tuple<std::shared_pointer<a>,
           std::shared_pointer<b>>

How would I achieve this with C++17? C++20 answers interesting but not applicable in my current situation.

Motivation: I've got a class that's going to be parameterized by a list of arbitrary and not necessarily unique types, and would like to have a std::function member for each type in the list.

like image 705
David Avatar asked Oct 22 '25 02:10

David


1 Answers

Template specialization is probably the easiest way.

template <typename T>
struct functionize;

template <typename... Ts>
struct functionize<std::tuple<Ts...>> {
    using type = std::tuple<std::function<void(Ts)>...>;
}

using MyTuple = std::tuple<int, double, char>;
using MyTupleFunctionized = typename functionize<MyTuple>::type;

To make it more generic, you can accept a template template parameter to apply to the pack.

template <typename Tuple, template <typename> typename Component>
struct transform_tuple;

template <typename... Ts, template <typename> typename Component>
struct transform_tuple<std::tuple<Ts...>, Component> {
    using type = std::tuple<Component<Ts>...>;
}

using MyTuple = std::tuple<int, double, char>;
using MyTransformedTuple = typename transform_tuple<MyTuple, std::shared_ptr>::type;

The more general solution works best with c++17 or later. Prior to that it would only match on a template with exactly 1 parameter. After c++17 it would also be able to match on something like std::vector which has 2 template parameters where the second one has a default argument.

Edit:
As pointed out in the comments by @Jarod42 and @Caleth we can improve on this a bit more.

template <typename Tuple, template <typename...> typename Component>
struct transform_tuple;

A parameter pack for the template template parameter allows us to pass something like a std::vector in from c++11 and forward. That only leaves issues if we want to pass something that mixes type and non-type parameters, like std::array.

We can partially solve that issue by using template aliases.

template <typename T>
using FixedArray10 = std::array<T, 10>;

using MyTransformedTuple = typename transform_tuple<MyTuple, FixedArray10>::type;
like image 64
super Avatar answered Oct 24 '25 17:10

super



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!