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

Convert constexpr struct to a runtime one


I'm trying to use a template class (here Foo), with a basic type like:

  • hana::tuple<hana::pair<hana::type<int>, Runtime>> with Runtime a class which obiviously can't be constepxr.

But the type can be construct in several way that's why I use:

  • hana::tuple<hana::pair<hana::type<int>, hana::type<Runtime>>> to do the work at compile time.

So the question is basically how convert from the first tuple type to the second one. I wonder if there is something in hana that could help me. Or even better, some tips on that kind of "conversion".

namespace hana = boost::hana;
using namespace hana::literals;

struct Runtime { std::vector<int> data; };

template < typename T >
struct Foo {
  T data;
};

constexpr decltype(auto)  convertMap(auto storageMap) {

return hana::make_type(hana::transform(
    storageMap,
    [] (auto pair) {
      return hana::make_pair(
        hana::first(pair),
        typename decltype(hana::typeid_(hana::second(pair)))::type {});
    }));
}

int main() {

  constexpr auto map = hana::make_tuple(
      hana::make_pair(hana::type_c<int>, hana::type_c<Runtime>)
      );

  constexpr auto result = convertMap(map);
  static_assert(result ==
     hana::type_c<hana::tuple<hana::pair<hana::type<int>, Runtime>>>);

  Foo<typename decltype(result)::type> test;
}

As you can see I tried some c++1z convertMap with hana::transform and lambdas, but the second tuple can't be constexpr, so I can't pass it to hana::make_type hoping to get a hana::type_c.

test.cpp: In function ‘int main()’:
test.cpp:70:41: error: ‘constexpr decltype(auto) convertMap(auto:27) [with auto:27 = boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, boost::hana::type_impl<Runtime>::_> >]’ called in a constant expression
   constexpr auto result = convertMap(map);
                                         ^
test.cpp:53:27: note: ‘constexpr decltype(auto) convertMap(auto:27) [with auto:27 = boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, boost::hana::type_impl<Runtime>::_> >]’ is not usable as a constexpr function because:
 constexpr decltype(auto)  convertMap(auto storageMap) {
                           ^~~~~~~~~~
test.cpp:53:27: error: temporary of non-literal type ‘boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, Runtime> >’ in a constant expression
In file included from /usr/include/boost/hana/detail/struct_macros.hpp:29:0,
                 from /usr/include/boost/hana/adapt_adt.hpp:15,
                 from lib/hana/include/boost/hana.hpp:59,
                 from test.cpp:1:
/usr/include/boost/hana/tuple.hpp:68:12: note: ‘boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, Runtime> >’ is not literal because:
     struct tuple
            ^~~~~
/usr/include/boost/hana/tuple.hpp:68:12: note:   ‘boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, Runtime> >’ has a non-trivial destructor

Solution

  • All you care about is a type - you can therefore hide the computation of the type in a non-constexpr implementation function, and afterwards call it with decltype(...){} to "force constexpr":

    template <typename T>
    decltype(auto) convertMapImpl(T storageMap)
    {
        return hana::make_type(hana::transform(storageMap, [](auto pair) {
            return hana::make_pair(hana::first(pair),
                typename decltype(hana::typeid_(hana::second(pair)))::type{});
        }));
    }
    
    template <typename T>
    constexpr decltype(auto) convertMap(T storageMap)
    {
        return decltype(convertMapImpl(storageMap)){};
    }
    

    live wandbox example


    Also note that using auto in a function signature is a gcc extension - you should use a template parameter instead to be standard-compliant.