Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define std::tuple<...> with number of arguments

I need to interface with code that uses (misuses) tuples on the form:

using Tuple = std::tuple<double, double, double, double, int, int, int, int>; // etc..

I wonder if there is a way to define this tuples with templates instead in the form:

using Tuple = SomeType<4, double, 4, int>::Type; // expands to the above tuple

And specify the number of arguments instead of specify each type.

I would prefer to only use std libs, but boost is accepted. And I need it to work with c++14. It is easy to do with macros, but I already have a macro solution, and I do not like having macros in the code for this kind of code.

The tuple above is a small example. In reality the tuples is much larger, contains many more types and more of each type.

like image 728
Lasersköld Avatar asked Dec 16 '20 10:12

Lasersköld


2 Answers

I am not aware of some std facility that does what you want directly.

The tricky part is to have a variadic template with mixed non-type and type parameters. The issue can be avoided by using a helper:

template <typename T,size_t reps>
struct repeated_type {
    using type = T;
    static const size_t N = reps;
};

Usage can look like this:

int main() {
    using two_ints = repeated_type<int,2>;
    using three_doubles = repeated_type<double,3>;
    using my_tuple = n_tuple<two_ints,three_doubles>;
    static_assert(std::is_same<
                         std::tuple<int,int,double,double,double>,
                         my_tuple
                  >::value);
}

Using a std::index_sequence we can get a std::tuple<T,T,T,...T> via std::make_tuple

template <typename T,std::size_t...n>
auto n_tuple_impl(std::index_sequence<n...>){ 
    return std::make_tuple( (n,T{})...); 
}

template <typename repT>
using n_tuple_single = decltype( 
                           n_tuple_impl<typename repT::type>(
                               std::make_index_sequence<repT::N>() 
                           )
                       );

Concatenetating several of them can be done via std::tuple_cat:

template <typename...repT>
using n_tuple = decltype( 
                    std::tuple_cat( 
                        ( n_tuple_single<repT>() )... 
                    ) 
                );

Live Demo.

In two places I require default construction. If needed this could be circumvented via std::declval. No instances are actually created.

For the interested reader, here is a rather messy C++11 implementation that is based on recursion instead of std::index_sequence and fold expressions to achieve the same.

like image 195
463035818_is_not_a_number Avatar answered Oct 04 '22 05:10

463035818_is_not_a_number


Here is a short solution using Boost.MP11 and can work with any type list holder (e.g. std::tuple, boost::mp11::mp_list, etc).

#include <boost/mp11/algorithm.hpp>

#include <cstddef>
#include <tuple>
#include <type_traits>

template<class T, std::size_t N, template<class...> class THolder = std::tuple>
struct repeater {
    using type = boost::mp11::mp_repeat_c<THolder<T>, N>;
};

template<class T, std::size_t N>
using repeater_t = typename repeater<T, N>::type;

int main() {
    using expected_t = std::tuple<double, double, double, double, int, int, int, int>;
    using actual_t = boost::mp11::mp_append<repeater_t<double, 4>, repeater_t<int, 4>>;
    static_assert(std::is_same_v<actual_t, expected_t>);
}

Here, we leverage boost::mp11::mp_repeat_c to build the type holder holding N types of type T. The type list we default to is std::tuple as requested. Then, at the call site, it's just a matter of appending the tuples together via boost::mp11::mp_append to get down to one tuple.

Wandbox link

like image 35
Joe Avatar answered Oct 04 '22 07:10

Joe