How to define a tuple of value types from a parameter pack
Asked Answered
G

2

11

I need to build a tuple of n types. These n types are value types of n other types. Consider this snippet:

#include <boost/hana.hpp>

namespace hana = boost::hana;

template<class... Types>
class CartesianProduct
{
public:
    CartesianProduct(Types... args) : sets(args...) {}

    hana::tuple<Types...> sets;
    hana::tuple<Types...::value_type> combination; // does not work obviously... but wo can this be done?
};

The application of this is intended like so: I pass this class a parameter pack of containers of possibly different types. The class puts these containers into a tuple sets. The class also has a field combination which is a tuple of as many elements as containers were passed to the class. But the types of the elements are the value types of the different containers.

The class is then intended to lazyly build the cartesian product of the containers passed to it and store the current combination in combination. But how can I actually get to the value types of the containers in a variadic fashion?

Gobble answered 14/6, 2017 at 6:8 Comment(3)
Do all the types have a value_type?Otha
Well, I make this a precondition.Gobble
If you do write this lazy cartesian product class, it would be awesome if you could contribute it back to Hana. I'm looking to add lazy views, and it might be a good idea to implement cartesian_product lazily by itself.Chatterton
O
14

It can be done, of course. You just need to declare the pack expansion appropriately.

hane::tuple<typename Types::value_type...> combination; 

Note the required use of the typename specifier. The rule of thumb is to treat the pack name as a single type. The same syntactic/semantic constraints apply, as we must specify that we access a type with the scope resolution operator. Then just tack the pack expansion at the end.

Live Example

#include <vector>
#include <map>
#include <tuple>

template<class... Types>
class CartesianProduct
{
public:
    CartesianProduct(Types... args) : sets(args...) {}

    std::tuple<Types...> sets;
    std::tuple<typename Types::value_type...> combination; 
};


int main() {
    std::vector<int> i;
    std::map<int, std::vector<int>> m;

    CartesianProduct<std::vector<int>, std::map<int, std::vector<int>>>
      c(i, m);

    return 0;
}
Otha answered 14/6, 2017 at 6:15 Comment(0)
Y
5

Expanding on StoryTeller's correct answer (accept his answer please):

I find it easier to visualise type translations like this by implementing them in terms of a translation meta-function, for example:

#include <vector>
#include <map>
#include <tuple>

namespace metafunction_impl
{
  // meta function taking one type (T) and 'returning' one type.
  // i.e. a unary metafunction
  template<class T> struct get_value_type
  {
    using result = typename T::value_type;
  };
}

// provide clean interface to the metafunction
template<class T> using GetValueType = typename metafunction_impl::get_value_type<T>::result;

template<class... Types>
class CartesianProduct
{
public:
    CartesianProduct(Types... args) : sets(args...) {}

    std::tuple<Types...> sets;

    // use my metafunction
    std::tuple<GetValueType<Types>...> combination; 
};


int main() {
    std::vector<int> i;
    std::map<int, std::vector<int>> m;

    CartesianProduct<std::vector<int>, std::map<int, std::vector<int>>>
      c(i, m);

    return 0;
}
Youngling answered 14/6, 2017 at 6:48 Comment(1)
That's a solid visualization of why one should imagine the pack as a single type name. Better than my rule of thumb approach. +1Otha

© 2022 - 2024 — McMap. All rights reserved.