Quentin's answer is very good, and what I'd go with.
The only downside is the "useless" generation of an index sequence [0, 1, 2, ...] whose values we ignore, and substitute for our own.
If we want to directly create the repeated values, we can write our own generator code (which is quite a bit more verbose):
Start with creating a type that can hold a number of std::size_t
values by aliasing a std::integer_sequence
:
template<std::size_t... vals>
using value_sequence = std::integer_sequence<std::size_t, vals...>;
The goal is to ultimately create a value_sequence<4, 4, 4>
and then instantiate an Eigen::Sizes
using those 4s.
The next thing we need to be able to do is concatenate two sequences, because we're going to build it up like so:
concat(value_sequence<4>, value_sequence<4>) --> value_sequence<4, 4>
We can do this via a stub method that accepts two value_sequence
types and returns the concatenated result. Note that we do not ever write a definition for this method; we're simply taking advantage of the type system to write less code than a template specialization would take:
template<std::size_t... lhs, std::size_t... rhs>
constexpr auto concat(value_sequence<lhs...>, value_sequence<rhs...>) -> value_sequence<lhs..., rhs...>;
At this point we have enough machinery to create a value_sequence<4,4,4>
, so now we need a way to indicate the value we wish to use (4) and the number of times to repeat it (3) to produce it:
template<std::size_t value, std::size_t num_repeats>
struct repeated_value
{
using left_sequence = value_sequence<value>;
using right_sequence = typename repeated_value<value, num_repeats-1>::type;
using type = decltype(concat(left_sequence{}, right_sequence{}));
};
repeated_value<4, 3>::type
produces a value_sequence<4, 4, 4>
.
Since repeated_value<...>::type
is recursive, we need to provide a base case via partial specialization:
template<std::size_t value>
struct repeated_value<value, 1>
{
using type = value_sequence<value>;
};
Great. All that's left is for us to receive an Eigen::Sizes
class and a value_sequence<4, 4, 4>
type, and produce Eigen::Sizes<4, 4, 4>
.
We can do this with partial template specialization again:
template<template<std::size_t...> class T, class...>
struct InstantiateWithRepeatedVals;
template<template<std::size_t...> class T, std::size_t... vals>
struct InstantiateWithRepeatedVals<T, value_sequence<vals...>>
{
using type = T<vals...>;
};
That it! Throw in a few helpers to make using it easier, and we're done:
template<std::size_t value, std::size_t num_repeats>
using repeated_value_t = typename repeated_value<value, num_repeats>::type;
template<template<std::size_t...> class T, std::size_t Value, std::size_t N>
using InstantiateWithRepeatedVals_t = typename InstantiateWithRepeatedVals<T, repeated_value_t<Value, N>>::type;
Now we can use it like so:
using my_type = InstantiateWithRepeatedVals_t<EigenSizes, 4, 3>;
static_assert(std::is_same_v<my_type, EigenSizes<4, 4, 4>>);