Search code examples
c++algorithmtuples

Mapping elementwise Tuple Using Template Function


I am using perform_mapping from link as here link, but I get compilation Error.

#include <string>
#include <tuple>
#include <utility>
#include <string>
#include <type_traits>

namespace details
{
    template <typename Tuple, typename Mapping>
    struct return_type;
    
    template <template <typename ...> typename Tuple, typename ... Types, typename Mapping>
    struct return_type<Tuple<Types...>, Mapping>
    {
        //I changed the below line from what is in the link
        using type = Tuple<decltype(std::invoke_result<Mapping, Types>())...>;
    };
    template <template <typename, std::size_t> typename Array, typename T, std::size_t Size, typename Mapping>
    struct return_type<Array<T, Size>, Mapping>
    {
        using type = Array<std::invoke_result_t<Mapping, T>, Size>;
    };
    
    template <typename Tuple, typename Mapping>
    using return_type_t = typename return_type<Tuple, Mapping>::type;
    
    template <typename Tuple, typename Mapping, std::size_t ... Indices>
    return_type_t<std::decay_t<Tuple>, std::decay_t<Mapping>> perform_mapping(Tuple&& tup, Mapping&& mapping, std::index_sequence<Indices...>)
    {
        return {mapping(std::get<Indices>(std::forward<Tuple>(tup)))...};
    }
}

template <typename Tuple, typename Mapping, 
          std::size_t Size = std::tuple_size<std::decay_t<Tuple>>::value>
auto perform_mapping(Tuple&& tup, Mapping&& mapping)
{
    return details::perform_mapping(std::forward<Tuple>(tup), std::forward<Mapping>(mapping), std::make_index_sequence<Size>{});
}

struct A
{
    A(double z) : x(z){}; 
    double x;
    using Type = double;
};

struct B
{
    B(std::string s) : x(std::move(s)) {}
    std::string x;
    using Type = std::string;
};

struct C
{
    C() : m_tuple({A(1.0), B("A")})
    {
    }

    template<class T>
    typename T::Type f(T& z)
    {
        return z.x;
    }

    std::tuple<A::Type, B::Type> get()
    {
        return perform_mapping(m_tuple, [this](auto& z) { return this->f(z); });
    }

    std::tuple<A, B> m_tuple;
};

int main()
{
    C c;
    auto t = c.get();
};

My question is: Can I implement perform_mapping in a way the code above works?


Solution

  • If you can use C++17, you can benefit from std::apply, which together with a variadic lambda is a very handy tool to "unpack" a tuple:

    template<typename Tuple, typename Mapping>
    auto perform_mapping(const Tuple& tup, Mapping&& mapping)
    {
        return std::apply([&](const auto&... vals) {
            return std::tuple(mapping(vals)...);
        }, tup);
    }
    

    (Perfect forwarding is left as an exercise.)