Search code examples
c++c++17boost-hana

How to transform a boost::hana::map into lambdas


I have the following code

template <typename T>
void my_func(T& /*var*/)
{
};

auto my_types = hana::make_map(
  hana::make_pair(hana::type_c<std::uint32_t>, hana::integral_c<std::uint8_t, 1>),
  hana::make_pair(hana::type_c<std::uint16_t>, hana::integral_c<std::uint8_t, 1>)
);

using my_variant = std::variant<std::uint32_t, std::uint16_t>;

auto to_factory = [](auto map)
{
  return hana::transform(map, [](auto pair)
    {
      return [](my_variant& value)
        {
          using T = typename decltype(hana::first(pair))::type;
          T v;
          my_func(v);
          value = v;
        };
    });
};

auto factory = to_factory(my_types);

but I always get the error message

error: 'boost::hana::type_impl<short unsigned int>::_&' is not a class, struct, or union type

when I use hana::pair in my_tuple. All works fine when I just use

auto my_types = hana::make_map(
  hana::type_c<std::uint32_t>,
  hana::type_c<std::uint16_t>
);

and obviously no call to hana::first

Why do I get back kind of a reference when using hana::first?


Solution

  • It's not clear why you are trying to use hana::map. hana::map is not a Functor and has no implementation for hana::transform. It is, however Foldable so you could use hana::unpack and return a new tuple of lambdas if you needed to.

    As for hana::first along with other accessors including hana::at, hana::at_key, etc.; They all return reference types so to access members you must strip the reference somehow.

    For this you can use the provided hana::type unary + operator:

    using type = typename decltype(+hana::first(x))::type;
    

    Or you can use hana::typeid_ which is a bit more readable in my opinion:

    using type = typename decltype(hana::typeid_(hana::first(x)))::type;
    

    I'm not sure if hana::map fits with your use case, but here is how you could "transform" it into a tuple of lambdas:

    #include <boost/hana.hpp>
    #include <cstdint>
    #include <variant>
    
    namespace hana = boost::hana;
    
    
    auto my_types = hana::make_map(
        hana::make_pair(hana::type_c<std::uint32_t>, hana::integral_c<std::uint8_t, 1>),
        hana::make_pair(hana::type_c<std::uint16_t>, hana::integral_c<std::uint8_t, 1>)
    );
    
    using my_variant = std::variant<std::uint32_t, std::uint16_t>;
    auto make_lambda = [](auto pair) {
        return [](my_variant value) {
            using T = typename decltype(hana::typeid_(hana::first(pair)))::type;
            // do stuff
        };
    };
    
    auto to_factory = [](auto map) {
        return hana::unpack(map, [](auto ...pairs) {
            return hana::make_tuple(make_lambda(pairs)...);
        });
    };
    
    // erm.. you could also do this
    auto to_factory_2 = [](auto map) {
        return hana::unpack(map, [](auto ...pairs) {
            return hana::make_tuple(((void)pairs, [](my_variant value) {
                using T = typename decltype(hana::typeid_(hana::first(pairs)))::type;
                // do stuff
            })...);
        });
    }
    

    https://godbolt.org/z/KMdrzS

    From your code example, note that you might also run into issues taking a mutable reference to a variant that is being implicitly cast from a different type so I just removed that.