Search code examples
c++boost-hana

How to wrap (compose) a boost hana map and access the bracket operator (operator[])?


I'm using a hana map (created using hana::make_map). I have a very simple class that would inherit from the hana map, and expose a second map as such


auto values = hana::make_map( ... );
auto uncertainties = hana::make_map( ... );

template< typename Values, typename Uncertainty >
struct map: Values{
  Uncertainty uncertainty;
  constexpr map(Values v, Uncertainty u ):
    Values( v ),
    uncertainty( u )
  { }
};

auto data = map( values, uncertainties );

// I want to do the following

auto hbar = data[ hbar ]; // Type hbar defined elsewhere
auto hbar_u = data.uncertainty[ hbar ]

This used to work. I recently updated our version of boost hana and now I get the following compiler error:

map.hpp:2:13: error: base
      'map_impl' is marked 'final'
struct map: Values{
            ^

If I understand this message correctly, boost hana has explicitly been marked so that I can no longer inherit.

What I really want to do is use the operator[] to access the values map and use .uncertainty to access the uncertainties map. How do I do this?

I really don't want to have to use any other boost library; hana is more than enough for my project.


Solution

  • It's typically preferred to use an aggregate vs inheritance.

    As "Mooing Duck" stated in the comments, you can just use a function to forward the operator[] to the values map.

    Looking at the implementation of operator[] in Boost.Hana you'll notice there is an overload for each value type since this cannot be deduced. (They should fix that)

    Casting values to the appropriate reference type will allow you to return references of the correct value type if that is needed. This will make it behave exactly like hana::map.

    #define BOOST_HANA_CONFIG_ENABLE_STRING_UDL 1
    #include <boost/hana.hpp>
    
    namespace hana = boost::hana;
    using namespace hana::literals;
    
    template <typename Values, typename Uncertainty>
    struct my_map_t {
      Values values;
      Uncertainty uncertainty;
      
      constexpr decltype(auto) operator[](auto&& key) & {
        return static_cast<Values&>(values)[
            std::forward<decltype(key)>(key)];
      }
      constexpr decltype(auto) operator[](auto&& key) && {
        return static_cast<Values&&>(values)[
            std::forward<decltype(key)>(key)];
      }
      constexpr decltype(auto) operator[](auto&& key) const& {
        return static_cast<Values const&>(values)[
            std::forward<decltype(key)>(key)];
      }
    };
    
    // Note: Clang 10 does not provide implicit deduction guides yet
    
    constexpr auto values = hana::make_map(
      hana::make_pair("foo"_s, 42) 
    );
    
    constexpr auto uncertainties = hana::make_map(
      hana::make_pair("bar"_s, 5)
    );
    
    
    constexpr auto my_map = my_map_t(values, uncertainties);
    
    static_assert(my_map["foo"_s] == 42);
    static_assert(my_map.uncertainty["bar"_s] == 5);
    
    int main() { }
    

    https://godbolt.org/z/XVNsy-


    If you want to use boring, old C++17:

    https://godbolt.org/z/i-NZd6