How to create a boost::tuple with the specified number of elements (of the same type)?
Asked Answered
A

3

7

Assume that I have the following class definition:

template <unsigned int N>
class foo
{
    boost::tuples::tuple<...> bar;
};

Given the compile-time constant N, I would like to expand the type of bar to be a tuple that holds N elements of a specified type. That is, the type of foo<2>::bar would be boost::tuples::tuple<T, T>. I'm guessing that I can use Boost.MPL for this, but I haven't figured out the exact sequence yet. I think I could do:

template <typename T, int N>
struct type_repeater
{
    typedef typename boost::mpl::fold<
        boost::mpl::range_c<T, 0, N>,
        boost::mpl::vector<>,
        boost::mpl::push_back<_1, T>
    >::type type;
};

So then for instance type_repeater<T, 2>::type would be equivalent to boost::mpl::vector<T, T>. I'm just not sure how/if I can take that type list and inject it into the argument list of a tuple, like I want. Is this possible?

Abott answered 20/12, 2013 at 20:49 Comment(2)
You don't need boost for this if you have access to C++11. Would you be OK with a version that uses C++11 instead?Cottonweed
I'm open to hearing the best solution for either case, but in the end I will need at least a C++03 implementation.Abott
I
3

Although this is totally doable with variadic templates and std::tuple, the best solution for what you want I think is to just use std::array. If you simply want a container with N instances of T, the std::array signature is already template <typename T, std::size_t N> class array. I think it fits your need exactly.

Having said that, if you really want std::tuple for some reason you can do it like so:

#include <tuple>

/* Forward declaration. */
template <std::size_t N, typename T>
class Tuple;

/* Convenience type alias. */
template <std::size_t N, typename T>
using TTuple = typename Tuple<N, T>::type;

/* Base case. */
template <typename T>
class Tuple<0, T> {
  public:

  using type = std::tuple<>;

}; // Tuple<0>

/* Recursive case. */
template <std::size_t N, typename T>
class Tuple {
  public:

  /* Note the use of std::declval<> here. */
  using type = decltype(std::tuple_cat(std::declval<std::tuple<T>>(),
                                       std::declval<TTuple<N - 1, T>>()));

};  // Tuple<N, T>

/* std::declval<> is necessary to support non default constructable classes. */
class NoDefault {
  public:

  NoDefault() = delete;

};  // Foo

/* Sample use. */
static_assert(std::is_same<TTuple<2, NoDefault>,
                           std::tuple<NoDefault, NoDefault>>::value, "");

int main() {}

Note: If you don't have access to C++11 but do have access to boost, boost::array and boost::tuples::tuple will do fine in lieu of std::array and std::tuple.

Imprecision answered 20/12, 2013 at 21:38 Comment(0)
C
5

This seems to be a good minimal example using C++11

#include <tuple>
template <unsigned int N, typename T>
struct type_repeater {
  typedef decltype(std::tuple_cat(std::tuple<T>(), typename type_repeater<N-1, T>::type())) type;
};

template <typename T>
struct type_repeater<0, T> {
  typedef decltype(std::tuple<>()) type;
};

int main() {
  type_repeater<3, float>::type asdf;
  std::get<0>(asdf);
  std::get<1>(asdf);
  std::get<2>(asdf);
}
Cottonweed answered 20/12, 2013 at 21:9 Comment(4)
Ok, I'm done screwing around with this answer I think.Cottonweed
This is fine, albeit it could be better since all it does is repeatedly concatenate tuples recursively.Cottager
This is the first way that came to mind, do you have any method in mind that doesn't use recursion?Cottonweed
@Rapptz: Link please?Heterozygote
I
3

Although this is totally doable with variadic templates and std::tuple, the best solution for what you want I think is to just use std::array. If you simply want a container with N instances of T, the std::array signature is already template <typename T, std::size_t N> class array. I think it fits your need exactly.

Having said that, if you really want std::tuple for some reason you can do it like so:

#include <tuple>

/* Forward declaration. */
template <std::size_t N, typename T>
class Tuple;

/* Convenience type alias. */
template <std::size_t N, typename T>
using TTuple = typename Tuple<N, T>::type;

/* Base case. */
template <typename T>
class Tuple<0, T> {
  public:

  using type = std::tuple<>;

}; // Tuple<0>

/* Recursive case. */
template <std::size_t N, typename T>
class Tuple {
  public:

  /* Note the use of std::declval<> here. */
  using type = decltype(std::tuple_cat(std::declval<std::tuple<T>>(),
                                       std::declval<TTuple<N - 1, T>>()));

};  // Tuple<N, T>

/* std::declval<> is necessary to support non default constructable classes. */
class NoDefault {
  public:

  NoDefault() = delete;

};  // Foo

/* Sample use. */
static_assert(std::is_same<TTuple<2, NoDefault>,
                           std::tuple<NoDefault, NoDefault>>::value, "");

int main() {}

Note: If you don't have access to C++11 but do have access to boost, boost::array and boost::tuples::tuple will do fine in lieu of std::array and std::tuple.

Imprecision answered 20/12, 2013 at 21:38 Comment(0)
H
1

Since you explicitly ask for a way to make mpl::vector into a runtime container, I recommend you stay Boosty and use Fusion's as_vector:

Given your initial example where you mol::fold to get an mpl::vector, you would then use:

boost::fusion::result_of::as_vector<
    mpl::vector<T, T>
>::type;

To get a Fusion Vector, which seems to be what you want. Boost Fusion fills the gap between the compile-time and runtime world.

Also, this is pre C++11, which is still important in many (maybe most?) projects.

Hyperboloid answered 30/12, 2013 at 17:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.