Search code examples
c++tuplesboost-hana

Boost::Hana tuple, best way to modify a value


I'm pretty new to this powerful library. I was surprised I couldn't find an easy way to, given a tuple, return another tuple that just modifies a single element, by index.

Moreover, I want this method to be available also at compile time

E.g.:

auto tup = hana::make_tuple(1_c,2_c,3_c);
static_assert(modify(tup,1,2000_c) == hana::make_tuple(1_c,2000_c,3_c));

I think I can achieve this result by combining insert and remove_at, but I'm wondering if there's a more efficient way to do so


Solution

  • Just to be clear to others who might find "modify a value" to mean changing the run-time value, hana::at and hana::at_c as well as the corresponding operator[] can return mutable references to change the value at run-time. This does not change the type of the element:

    hana::at_c<1>(tup) = 2000;
    

    https://godbolt.org/z/xErFNm

    As far as replacing the element type and all, Boost.Hana does not support this directly, but looking at the implementation of remove_at, implementing a replace_at is straight forward:

    #include <boost/hana.hpp>
    #include <utility>
    
    namespace hana = boost::hana;
    using namespace hana::literals;
    
    template <typename Xs, typename X, std::size_t ...before, std::size_t ...after>
    constexpr auto replace_at_helper(Xs&& xs, X&&x, std::index_sequence<before...>,
                                          std::index_sequence<after...>) {
      return hana::make_tuple(
          hana::at_c<before>(std::forward<Xs>(xs))...,
          std::forward<X>(x),
          hana::at_c<after + sizeof...(before) + 1>(std::forward<Xs>(xs))...);
    }
    
    template <std::size_t n>
    constexpr auto replace_at_c = [](auto&& xs, auto&& x) {
        constexpr auto len = decltype(hana::length(xs))::value;
        return replace_at_helper(static_cast<decltype(xs)>(xs),
                                 static_cast<decltype(x)>(x),
                                 std::make_index_sequence<n>{},
                                 std::make_index_sequence<len - n - 1>{});
    };
    
    auto tup = hana::make_tuple(1_c,2_c,3_c);
    static_assert(replace_at_c<1>(tup,2000_c) == hana::make_tuple(1_c,2000_c,3_c));
    

    https://godbolt.org/z/H2aySg

    This is more efficient than composing other functions that would rely on creating intermediate tuples to shuffle values around.