3

If I have a variant, like so:

using my_variant = boost::variant<int, bool, std::string>;

Is there an easy way to extract the types the variant can contain into a Boost.Hana tuple so that the following holds:

using boost::hana::type;
static_assert(std::is_same<my_tuple, boost::hana::tuple<type<int>, type<bool>, type<std::string>>>{});
Sam Kellett
  • 1,277
  • 12
  • 33
  • Just to clarify; do you want a `hana::tuple`, or a `hana::tuple, hana::type, hana::type>`. In other words, do you want a tuple of the __types__, or a tuple containing __objects__ whose types are that of the variant? – Louis Dionne Nov 12 '15 at 19:00
  • a tuple of types. edited the question :) – Sam Kellett Nov 12 '15 at 19:01
  • although something generic to do either, ala `meta::as_list` would be good (is there one? couldn't see it in the docs) – Sam Kellett Nov 12 '15 at 19:07
  • Nope, there's none right now. However, I've been thinking about this for a while, but I never really found a satisfying name for it: https://github.com/boostorg/hana/issues/67. However, we'll be able to use the fact that `boost::variant<>::types` is an MPL sequence. I'm writing up my answer. – Louis Dionne Nov 12 '15 at 19:25
  • If you need this functionality for arbitrary templates, let's discuss it on https://github.com/boostorg/hana/issues/67. I do agree that it would be useful, but I just need a little bit of convincing and some help with the naming of such a functionality. – Louis Dionne Nov 12 '15 at 19:47

3 Answers3

8

The following will work on develop (since e13d826):

#include <boost/hana.hpp>
#include <boost/hana/ext/boost/mpl.hpp>
#include <boost/variant.hpp>
#include <string>
namespace hana = boost::hana;


using my_variant = boost::variant<int, bool, std::string>;

constexpr auto my_tuple = hana::to<hana::tuple_tag>(my_variant::types{});

// Note:
// In general, don't use std::is_same to compare hana::tuple; use == in
// because it will also work if the tuple contains hana::basic_types.
static_assert(my_tuple == hana::tuple_t<int, bool, std::string>, "");

What e13d826 did was add support for mpl::list; only mpl::vector was supported before, and boost::variant<>::types is a mpl::list. This is why my answer took a while to come; I was implementing that :-).

Edit

I did not explain why I'm using constexpr auto my_tuple = ... instead of using my_tuple = decltype(...). Well, the reason is simply because knowing the type of my_tuple is not really useful, since you can't rely on it. Indeed, if you look at the documentation of hana::type, it's written that you can't rely on hana::type<T> being anything specific. There are good reasons for this, but from a usability point of view it means that you can't rely on the type of hana::tuple<hana::type<...>, ...> being anything specific either. When doing type-level computations, prefer value-level encoding (thus auto my_tuple = ...) to type-level encoding. This is the specificity of Hana over MPL & friends, and you should try to stick with it as much as possible, or you'll find Hana to be really clunky (because not written with that in mind).

Louis Dionne
  • 3,104
  • 1
  • 15
  • 35
  • this is amazing, thanks! although a solution for general typelists would think would be very helpful for integrating hana into any existing codebase / library – Sam Kellett Nov 12 '15 at 19:47
  • It just occurred to me that one should be careful with a general specialization-based solution. Indeed, some library components use an emulation of variadic templates, and `xxx` is instead `xxx`. I just checked, and `boost::variant` uses real variadic templates if the compiler supports it, so it would work now. But we must still be cautious in the general case. – Louis Dionne Nov 12 '15 at 20:33
2

This doesn't use any hana features, but should work.

First, a transcribe type function that takes a template, and an instance of a different template, and transcribes the types in the second into the first:

template<template<class...>class To, class From>
struct transcribe;
template<template<class...>class To, class From>
using transcribe_t=typename transcribe<To,From>::type;

template<template<class...>class Z, template<class...>class Src, class...Ts>
struct transcribe<Z, Src<Ts...>> {
  using type=Z<Ts...>;
};

Now, a template that takes types, and returns a hana tuple of hana types:

template<class...Ts>
using tuple_of_types = boost::hana::tuple<boost::hana::type<Ts>...>;

And we are done:

template<class Src>
using get_types_from = transcribe_t< tuple_of_types, Src >;

using result = get_types_from< my_variant >;

get_types_from is because a type function that extracts the template arguments of an arbitrary template seems useful.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
2

Some time back I had come across something similar to this for such conversion. Not able to find the actual source of this idea though or it may be even a pretty common practice:

template<class From, template<class...> class To> struct convert_impl;

template<template<class...> class From, class... Types, 
         template<class...> class To>
struct convert_impl<From<Types...>, To>
{
    using converted_type = To<Types...>;
};

template<class From, template<class...> class To>
using convert = typename convert_impl<From, To>::converted_type;

Since, I am not sure about boost hana tuple, I will show an example with std::tuple

convert<boost::variant<int, bool, string>, std::tuple>
Arunmu
  • 6,837
  • 1
  • 24
  • 46