C++11: Number of Variadic Template Function Parameters? - c++

How can I get a count of the number of arguments to a variadic template function?
ie:
template<typename... T>
void f(const T&... t)
{
int n = number_of_args(t);
...
}
What is the best way to implement number_of_args in the above?

Just write this:
const std::size_t n = sizeof...(T); //you may use `constexpr` instead of `const`
Note that n is a constant expression (i.e known at compile-time), which means you may use it where constant expression is needed, such as:
std::array<int, n> a; //array of n elements
std::array<int, 2*n> b; //array of (2*n) elements
auto middle = std::get<n/2>(tupleInstance);
Note that if you want to compute aggregated size of the packed types (as opposed to number of types in the pack), then you've to do something like this:
template<std::size_t ...>
struct add_all : std::integral_constant< std::size_t,0 > {};
template<std::size_t X, std::size_t ... Xs>
struct add_all<X,Xs...> :
std::integral_constant< std::size_t, X + add_all<Xs...>::value > {};
then do this:
constexpr auto size = add_all< sizeof(T)... >::value;
In C++17 (and later), computing the sum of size of the types is much simpler using fold expression:
constexpr auto size = (sizeof(T) + ...);

#include <iostream>
template<typename ...Args>
struct SomeStruct
{
static const int size = sizeof...(Args);
};
template<typename... T>
void f(const T&... t)
{
// this is first way to get the number of arguments
constexpr auto size = sizeof...(T);
std::cout<<size <<std::endl;
}
int main ()
{
f("Raje", 2, 4, "ASH");
// this is 2nd way to get the number of arguments
std::cout<<SomeStruct<int, std::string>::size<<std::endl;
return 0;
}

Related

Making an index sequence tuple

Is there a way to create index tuple with compile-time known size like
std::tuple<int, int, ...> tup = make_index_tuple(100); // -> tup == (0, 1, 2, ..., 99)
Maybe somehow using std::make_index_sequence?
There is a kind of similar question about uninitialized tuple type but with structures involved
EDIT
I am trying to test my hand-written string formatting function with a signature like this
template<class... Args>
std::string format(std::string_view fmt, const Args&... args)
so I implemented a test, that requires to pass a sequence of ints 0, 1, 2, 3, ..., 99 to args. If there is a way to create tuple like so, then I could use std::apply to pass required arguments. If there are other ways i'd be glad to hear them too :)
pass a sequence of ints 0, 1, 2, 3, ..., 99 to [function arguments]
You don't need tuples. Do this:
template <std::size_t ...I>
void foo(std::index_sequence<I...>)
{
format("foo", I...);
}
int main()
{
foo(std::make_index_sequence<42>());
}
If you insist on std::apply, it understands std::array out of the box. You just need to handle the first parameter separately, since it's not an int.
const int n = 32;
std::array<int, n> array;
for (int i = 0; i < n; i++)
array[i] = i;
std::apply([](auto ...params){format("foo", params...);}, array);
For educational purposes, here's the answer to the original question. This is how you make a tuple:
template <typename T, typename I>
struct n_tuple_helper {};
template <typename T, std::size_t ...I>
struct n_tuple_helper<T, std::index_sequence<I...>>
{
using type = std::tuple<std::enable_if_t<I || true, T>...>;
};
template <typename T, std::size_t N>
using n_tuple = typename n_tuple_helper<T, std::make_index_sequence<N>>::type;
Now, n_tuple<int, 3> expands to std::tuple<int, int, int>
Above have mentioned how you could accomplish your idea by creating std::array. In case you still want to see how you should create a tuple:
template<typename T, T ...I>
auto make_tuple_sequence_helper(std::integer_sequence<T, I...>)
{
return std::make_tuple(I...);
}
template<std::size_t I, typename T>
auto make_tuple_sequence()
{
return make_tuple_sequence_helper(std::make_integer_sequence<T, I>());
}
Then you call it in main:
auto int_tuple = make_tuple_sequence<5, int>();
// Equivalent to `std::make_tuple(0,1,2,3,4)`
auto long_tuple = make_tuple_sequence<5, long>();
// Equivalent to `std::make_tuple(0l,1l,2l,3l,4l)`
auto size_t_tuple = make_tuple_sequence<5, std::size_t>();
// Equivalent to `std::make_tuple(0z,1z,2z,3z,4z)`
Note that you could also remove the typename T, if you know you only want tuple<int...> :
template<int ...I>
auto make_tuple_sequence_helper(std::integer_sequence<int, I...>)
{
return std::make_tuple(I...);
}
template<std::size_t I,>
auto make_tuple_sequence()
{
return make_tuple_sequence_helper(std::make_integer_sequence<int, I>());
}
And in main would be:
auto int_tuple = make_tuple_sequence<5>();
While the accepted answer should work for you too, I think it can still be improved. Since your goal appears to be calling the format function with an array of arguments, we will try solving this issue directly.
// declaration of format
template <typename ...Args>
std::string format(std::string_view fmt, const Args &...args);
// variadic template which uses type deduction to obtain the indices as template parameters
template <typename T, std::size_t ...I>
std::string format_arr_impl(std::string_view fmt,
T arr[sizeof...(I)],
std::index_sequence<I...>)
{
return format(fmt, arr[I]...);
}
// wrapper function, so that we don't have to call `std::make_index_sequence` each time
// this accepts a builtin array, but we can also add an overload for std::array
template <typename T, std::size_t N>
std::string format_arr(std::string_view fmt, T (&arr)[N])
{
return format_arr_impl(fmt, arr, std::make_index_sequence<N>());
}
// we can now call format for an array very easily
int main()
{
int data[] {11, 22, 33};
format_arr("{} {} {}", data);
}
Or alternatively, if you really insist on creating a tuple, we can also do the following:
// make use of the same index_sequence trick to deduce the indices
template <typename T, std::size_t ...I>
auto tuple_from_arr_impl(T arr[sizeof...(I)], std::index_sequence<I...>)
{
return std::make_tuple(arr[I]...);
}
// once again, we could also use a std::array instead of a reference to a builtin array
template <typename T, std::size_t N>
auto tuple_from_arr(std::string_view fmt, T (&arr)[N])
{
return tuple_from_arr_impl(fmt, arr, std::make_index_sequence<N>());
}
With the latter approach, we obtain a tuple where each member is an element of the array. We could then use std::apply to print the members.

intializing a std::array of objects that don't have default constructor from another constexpr std::array

Is it possible to initialize an array of objects whose members are initialized from another constexpr array of trivial objects. For example I have the following
struct X
{
X(int y): _x(y){}
int _x;
};
struct Z
{
static constexpr std::array<int, 4> arr = {1,6,0,4};
// How does one implement make_array below that constructs
// X[0] from arr[0], X[1] from arr[1], etc.
// Is it even feasible in C++14/17?
std::array<X, arr.size()> xArr = make_array( );
};
With std::index_sequence:
template <typename T, typename U, std::size_t N, std::size_t ... Is>
constexpr std::array<T, N> make_array(const std::array<U, N>& a, std::index_sequence<Is...>)
{
return {{T(a[Is])...}};
}
template <typename T, typename U, std::size_t N>
constexpr std::array<T, N> make_array(const std::array<U, N>& a)
{
return make_array<T>(a, std::make_index_sequence<N>());
}
Usage:
static constexpr std::array<int, 4> arr = {1,6,0,4};
/*constexpr*/ std::array<X, arr.size()> xArr = make_array<X>(arr);
Demo
One solution is to use a parameter pack and expand it to construct an initializer list for the array.
I use a std::index_sequence and std::make_index_sequence to construct a parameter pack which contains the index of the elements (simply 0, 1, 2, ..., N-1) then I unpack those indexes into an initializer list :
#include <array>
#include <utility>
// O : output type; type to convert elements to
// T : input type
// N : number of elements
// I : parameter pack of indexes
template<class O, class T, std::size_t N, std::size_t ... I>
auto make_array_impl(const std::array<T, N> & p_input, std::index_sequence<I...>) -> std::array<O, N>
{
// Unpack the parameter pack into an initializer list
// Constructs an `O` from each element in order
return {O{p_input[I]}...};
}
// O : output type; type to convert elements to
// T : input type
// N : number of elements
template<class O, class T, std::size_t N>
auto make_array(const std::array<T, N> & p_input)
{
// Helper function to automatically generate the parameter pack
return make_array_impl<O>(p_input, std::make_index_sequence<N>{});
}
Example : https://godbolt.org/z/dhEGaG

Variadic template. Size of the template arguement list in variadic template data structure [duplicate]

How can I get a count of the number of arguments to a variadic template function?
ie:
template<typename... T>
void f(const T&... t)
{
int n = number_of_args(t);
...
}
What is the best way to implement number_of_args in the above?
Just write this:
const std::size_t n = sizeof...(T); //you may use `constexpr` instead of `const`
Note that n is a constant expression (i.e known at compile-time), which means you may use it where constant expression is needed, such as:
std::array<int, n> a; //array of n elements
std::array<int, 2*n> b; //array of (2*n) elements
auto middle = std::get<n/2>(tupleInstance);
Note that if you want to compute aggregated size of the packed types (as opposed to number of types in the pack), then you've to do something like this:
template<std::size_t ...>
struct add_all : std::integral_constant< std::size_t,0 > {};
template<std::size_t X, std::size_t ... Xs>
struct add_all<X,Xs...> :
std::integral_constant< std::size_t, X + add_all<Xs...>::value > {};
then do this:
constexpr auto size = add_all< sizeof(T)... >::value;
In C++17 (and later), computing the sum of size of the types is much simpler using fold expression:
constexpr auto size = (sizeof(T) + ...);
#include <iostream>
template<typename ...Args>
struct SomeStruct
{
static const int size = sizeof...(Args);
};
template<typename... T>
void f(const T&... t)
{
// this is first way to get the number of arguments
constexpr auto size = sizeof...(T);
std::cout<<size <<std::endl;
}
int main ()
{
f("Raje", 2, 4, "ASH");
// this is 2nd way to get the number of arguments
std::cout<<SomeStruct<int, std::string>::size<<std::endl;
return 0;
}

Passing std::array argument with size restricted to expandable set of sizes

How would one best implement a single function that accepts two std::array<int, [size]> arguments, each with a size constrained by a corresponding set of values known at compile-time?
The function must only accept arrays with sizes derived from a given set (enum/macro/etc)
The sets of allowable array "sizes" may be changed in the future and may be large (effectively precluding function overloading)
The function itself should remain fixed regardless of changes to the sets of allowable array "sizes"
The question "Passing a std::array of unknown size to a function", while similar, doesn't appear to directly apply.
The following works in C++14 but seems unnecessarily redundant & messy:
#include <type_traits>
#include <array>
// Add legal/allowable sizes for std::array<> "types" here
// Note: Not married to this; perhaps preprocessor instead?
enum class SizesForArrayX : size_t { Three = 3, Four, Forty = 40 };
enum class SizesForArrayY : size_t { Two = 2, Three, EleventyTwelve = 122 };
// Messy, compile-time, value getter for the above enum classes
template <typename S>
constexpr size_t GetSizeValue(const S size)
{ return static_cast<std::underlying_type_t<S>>(size); }
// An example of the function in question; is Template Argument Deduction
// possible here?
// Note: only arrays of "legal"/"allowable" sizes should be passable
template <SizesForArrayX SX, SizesForArrayY SY>
void PickyArrayHandler(
const std::array<int, GetSizeValue(SX)>& x,
const std::array<int, GetSizeValue(SY)>& y)
{
// Do whatever
for (auto& i : x) i = 42;
for (auto& i : y) while (i --> -41) i = i;
}
Calling the above:
int main()
{
// Declare & (value-)initialize some arrays
std::array<int, GetSizeValue(SizesForArrayX::Forty)> x{};
std::array<int, GetSizeValue(SizesForArrayY::Two>) y{};
//PickyArrayHandler(x, y); // <- Doesn't work; C2672, C2783
// This works & handles arrays of any "allowable" size but the required
// template params are repetitions of the array declarations; ick
PickyArrayHandler<SizesForArrayX::Forty, SizesForArrayY::Two>(x, y);
}
...which is ugly, inelegant, slow-to-compile, and requires the declared array size match the explicit "size" passed to the PickyArrayHandler function template.
For the specific example above: Is there a way for the PickyArrayHandler template to deduce the sizes of the passed arrays?
Generally speaking: Is there a different, better approach?
Since you don't seem to be picky about how the valid sizes are defined, you can use type traits
#include <array>
template <size_t N> struct valid_size1 { enum { value = false }; };
template <size_t N> struct valid_size2 { enum { value = false }; };
template <> struct valid_size1<3> { enum { value = true }; };
template <> struct valid_size1<4> { enum { value = true }; };
template <> struct valid_size1<40> { enum { value = true }; };
template <> struct valid_size2<2> { enum { value = true }; };
template <> struct valid_size2<122> { enum { value = true }; };
template <size_t TX, size_t TY>
void PickyArrayHandler(const std::array<int, TX> &x,
const std::array<int, TY> &y)
{
static_assert(valid_size1<TX>::value, "Size 1 is invalid");
static_assert(valid_size2<TY>::value, "Size 2 is invalid");
// Do whatever
}
int main()
{
// Declare & (value-)initialize some arrays
std::array<int, 40> x{};
std::array<int, 2> y{};
PickyArrayHandler(x, y);
PickyArrayHandler(std::array<int, 4>{}, std::array<int, 2>{});
// PickyArrayHandler(std::array<int, 1>{}, std::array<int, 5>{}); // BOOM!
}
Here's a solution using an array:
#include <iostream>
#include <array>
constexpr size_t valid_1[] = { 3, 4, 40 };
constexpr size_t valid_2[] = { 2, 122 };
template <size_t V, size_t I=0>
struct is_valid1 { static constexpr bool value = V==valid_1[I] || is_valid1<V,I+1>::value; };
template <size_t V, size_t I=0>
struct is_valid2 { static constexpr bool value = V==valid_2[I] || is_valid2<V,I+1>::value; };
template <size_t V>
struct is_valid1<V, sizeof(valid_1)/sizeof(valid_1[0])>
{static constexpr bool value = false; };
template <size_t V>
struct is_valid2<V, sizeof(valid_2)/sizeof(valid_2[0])>
{static constexpr bool value = false; };
template <size_t TX, size_t TY>
void PickyArrayHandler(const std::array<int, TX> &x,
const std::array<int, TY> &y)
{
static_assert(is_valid1<TX>::value, "Size 1 is invalid");
static_assert(is_valid2<TY>::value, "Size 2 is invalid");
// Do whatever
}
twiddled around a bit and got this reduced one working: maybe it helps:
enum SizesForArrayX : size_t { Three = 3, Four, Forty = 40 };
enum SizesForArrayY : size_t { Two = 2, EleventyTwelve = 122 };
template <size_t TX, size_t TY>
void PickyArrayHandler(
const std::array<int, TX>& x,
const std::array<int, TY>& y)
{
// Do whatever
}
int main()
{
// Declare & (value-)initialize some arrays
std::array<int, SizesForArrayX::Forty> x{};
std::array<int, SizesForArrayY::Two> y{};
PickyArrayHandler(x, y);
return 0;
}
Unfortunately, your enums are not continuous so you cannot simply iterate over the enum and you have to handle all cases individually. Since the sizes are known at compile-time you can static_assert for it.
#include <array>
enum SizesForArrayX : size_t { Three = 3, Four, Forty = 40 };
enum SizesForArrayY : size_t { Two = 2, EleventyTwelve = 122 };
template <size_t TX, size_t TY>
void PickyArrayHandler(const std::array<int, TX> &x,
const std::array<int, TY> &y)
{
static_assert(TX == Three || TX == Four || TX == Forty,
"Size mismatch for x");
static_assert(TY == Two || TY == EleventyTwelve, "Size mismatch for y");
// Do whatever
}
int main()
{
// Declare & (value-)initialize some arrays
std::array<int, SizesForArrayX::Forty> x{};
std::array<int, SizesForArrayY::Two> y{};
PickyArrayHandler(x, y);
PickyArrayHandler(std::array<int, 4>{}, std::array<int, 2>{});
//PickyArrayHandler(std::array<int, 1>{}, std::array<int, 5>{}); // BOOM!
}
The best way I see to solve this problem is writing a custom type trait:
template <std::underlying_type_t<SizesForArrayX> SX>
struct is_size_x {
static constexpr bool value = false;
};
template <>
struct is_size_x<static_cast<std::underlying_type_t<SizesForArrayX>>(SizesForArrayX::Forty)>{
static constexpr bool value = true;
};
I'd put these right under the enum class declarations, just so it's easy to check that you got them all. Somebody more clever than I could probably figure out a way to even do this with variadic templates so you only need one specialization.
While tedious, if you have a small set of values this should be fast enough and easy to put in unit tests. The other nice thing about this approach is that if you have multiple functions that need one of these special sizes, you don't have to copy/paste static_asserts around.
With the type traits, your function becomes trivial:
template <std::size_t SX, std::size_t SY>
void PickyArrayHandler(
std::array<int, SX>& x,
std::array<int, SY>& y)
{
static_assert(is_size_x<SX>::value, "Invalid size SX");
static_assert(is_size_y<SY>::value, "Invalid size SY");
// Do whatever
for (auto& i : x) i = 42;
for (auto& i : y) while (i --> -41) i = i;
}
Lastly, you can make a type alias to avoid creating invalid arrays in the first place:
template <typename T, SizesForArrayX SIZE>
using XArray =
std::array<T, static_cast<std::underlying_type_t<SizesForArrayX>>(SIZE)>;
template <typename T, SizesForArrayY SIZE>
using YArray =
std::array<T, static_cast<std::underlying_type_t<SizesForArrayY>>(SIZE)>;
That'll prevent you from declaring an array if it's not an approved size:
XArray<int, SizesForArrayX::Forty> x{};
YArray<int, SizesForArrayY::Two> y{};
Personally I would just manually type the allowable sizes into a static_assert inside PickyArrayHandler. If that's not an option because the sizes will be used in other parts of your program and you're adhering to the DRY principal then I'd use the preprocessor.
#define FOREACH_ALLOWABLE_X(SEP_MACRO) \
SEP_MACRO(3) \
SEP_MACRO(4) \
SEP_MACRO(40) \
#define FOREACH_ALLOWABLE_Y(SEP_MACRO) \
SEP_MACRO(2) \
SEP_MACRO(3) \
SEP_MACRO(122) \
#define COMMA_SEP(NUM) NUM,
#define LOGIC_OR_SEP_X(NUM) N1 == NUM ||
#define LOGIC_OR_SEP_Y(NUM) N2 == NUM ||
#define END_LOGIC_OR false
// some arrays with your sizes incase you want to do runtime checking
namespace allowable_sizes
{
size_t x[] {FOREACH_ALLOWABLE_X(COMMA_SEP)};
size_t y[] {FOREACH_ALLOWABLE_Y(COMMA_SEP)};
}
template <size_t N1, size_t N2>
void PickyArrayHandler(const std::array<int, N1>& x, const std::array<int, N2>& y)
{
static_assert(FOREACH_ALLOWABLE_X(LOGIC_OR_SEP_X) END_LOGIC_OR);
static_assert(FOREACH_ALLOWABLE_Y(LOGIC_OR_SEP_Y) END_LOGIC_OR);
// do whatever
}
#undef FOREACH_ALLOWABLE_X
#undef FOREACH_ALLOWABLE_Y
#undef COMMA_SEP
#undef LOGIC_OR_SEP_X
#undef LOGIC_OR_SEP_Y
#undef END_LOGIC_OR
Some C++ purists will frown at it but it gets the job done.
You could have a is_of_size-like template that check the size of the array, and then use it to disable the template if one of the sizes does not match, something like:
#include <array>
#include <type_traits>
// Forward template declaration without definition.
template <class T, T N, T... Sizes>
struct is_one_of;
// Specialization when there is a single value: Ends of the recursion,
// the size was not found, so we inherit from std::false_type.
template <class T, T N>
struct is_one_of<T, N>: public std::false_type {};
// Generic case definition: We inherit from std::integral_constant<bool, X>, where X
// is true if N == Size or if N is in Sizes... (via recursion).
template <class T, T N, T Size, T... Sizes>
struct is_one_of<T, N, Size, Sizes... >:
public std::integral_constant<
bool, N == Size || is_one_of<T, N, Sizes... >::value> {};
// Alias variable template, for simpler usage.
template <class T, T N, T... Sizes>
constexpr bool is_one_of_v = is_one_of<T, N, Sizes... >::value;
template <std::size_t N1, std::size_t N2,
std::enable_if_t<
(is_one_of_v<std::size_t, N1, 3, 4, 40>
&& is_one_of_v<std::size_t, N2, 2, 3, 122>), int> = 0>
void PickyArrayHandler(
const std::array<int, N1>& x,
const std::array<int, N2>& y)
{
}
Then you can simply:
PickyArrayHandler(std::array<int, 3>{}, std::array<int, 122>{}); // OK
PickyArrayHandler(std::array<int, 2>{}, std::array<int, 3>{}); // NOK
In C++17, you could (I think) replace is_one_of with:
template <auto N, auto... Sizes>
struct is_one_of;
...and automatically deduce std::size_t.
In C++20, you could use a concept to have clearer error messages ;)
Using static_assert for invalid sizes is not a good solution because it doesn't play well with SFINAE; i.e., TMP facilities like std::is_invocable and the detection idiom will return false positives for calls that in fact always yield an error. Far better is to use SFINAE to remove invalid sizes from the overload set, resulting in something resembling the following:
template<std::size_t SX, std::size_t SY,
typename = std::enable_if_t<IsValidArrayXSize<SX>{} && IsValidArrayYSize<SY>{}>>
void PickyArrayHandler(std::array<int, SX> const& x, std::array<int, SY> const& y) {
// Do whatever
}
First we need to declare our valid sizes; I don't see any benefit to stronger typing here, so for a compile-time list of integers, std::integer_sequence works just fine and is very lightweight:
using SizesForArrayX = std::index_sequence<3, 4, 40>;
using SizesForArrayY = std::index_sequence<2, 3, 122>;
Now for the IsValidArraySize traits... The straightforward route is to make use of C++14's relaxed-constexpr rules and perform a simple linear search:
#include <initializer_list>
namespace detail {
template<std::size_t... VSs>
constexpr bool idx_seq_contains(std::index_sequence<VSs...>, std::size_t const s) {
for (auto const vs : {VSs...}) {
if (vs == s) {
return true;
}
}
return false;
}
} // namespace detail
template<std::size_t S>
using IsValidArrayXSize
= std::integral_constant<bool, detail::idx_seq_contains(SizesForArrayX{}, S)>;
template<std::size_t S>
using IsValidArrayYSize
= std::integral_constant<bool, detail::idx_seq_contains(SizesForArrayY{}, S)>;
Online Demo
However if compile times are at all a concern, I suspect the following will be better, if potentially less clear:
namespace detail {
template<bool... Bs>
using bool_sequence = std::integer_sequence<bool, Bs...>;
template<typename, std::size_t>
struct idx_seq_contains;
template<std::size_t... VSs, std::size_t S>
struct idx_seq_contains<std::index_sequence<VSs...>, S>
: std::integral_constant<bool, !std::is_same<bool_sequence<(VSs == S)...>,
bool_sequence<(VSs, false)...>>{}>
{ };
} // namespace detail
template<std::size_t S>
using IsValidArrayXSize = detail::idx_seq_contains<SizesForArrayX, S>;
template<std::size_t S>
using IsValidArrayYSize = detail::idx_seq_contains<SizesForArrayY, S>;
Online Demo
Whichever implementation route is chosen, using SFINAE in this way enables very nice error messages – e.g. for PickyArrayHandler(std::array<int, 5>{}, std::array<int, 3>{});, current Clang 7.0 ToT yields the following, telling you which array's size is invalid:
error: no matching function for call to 'PickyArrayHandler'
PickyArrayHandler(std::array<int, 5>{}, std::array<int, 3>{});
^~~~~~~~~~~~~~~~~
note: candidate template ignored: requirement 'IsValidArrayXSize<5UL>{}' was not satisfied [with SX = 5, SY = 3]
void PickyArrayHandler(std::array<int, SX> const& x, std::array<int, SY> const& y) {
^

Spliting a std::array into a tuple of smaller sized std::array

I'm trying to split a std::array<T, N> into a tuple of smaller arrays, like std::tuple<std::array<T, N1>, std::array<T, N2>, ...> where N1 + N2 + ... = N.
namespace detail {
// Summation of the given values
template <class T>
constexpr T sum(const T& x) { return x; }
template <class T, class ...Args>
constexpr auto sum(const T& x, Args&&... args)
{ return x + sum(std::forward<Args>(args)...); }
}
template <class T, std::size_t... Ns>
constexpr
std::tuple<std::array<T, Ns>...>
f(const std::array<T, detail::sum(Ns...)>& x)
{
// How do I implement this function?
}
int main()
{
constexpr std::array<Foo, 5> arr = { ... };
constexpr auto t = f<Foo, 2,3>(arr);
}
Actually I already implemented f but it's based on a loop which simply creates an empty array and copies the elements of the given array, but it doesn't work if T is not default_constructible.
I tried to utilize std::integer_sequence and std::make_index_sequence, but I think i'm totally lost with no clue.
Can anyone help me implement the function please?
Write
template<class T,size_t...Is,size_t N>
std::array<T,sizeof...(Is)>
extract(std::array<T,N>const&,std::index_sequence<Is...>){
return {{arr[Is]...}};
}
now we just need to turn {1,2,3} into {{0},{1,2},{3,4,5}} roughly, with everything being C++ index sequences (so syntax).
Map {3,4,0} to {0,1,2} -- a count of indexes to subarrays. Then map {3,4,0} x 1 to {3,4,5,6} and similar for the others. That gives us the indexes inside the subarrays, which we feed to extract and bob is your uncle.
template<size_t n, size_t...counts>
constexpr auto
foo( std::index_sequence<counts...> )
-> offset_seq<
sum_n<n>(counts...),
std::make_index_sequence<get_n<n,counts...> >
>{ return {}; }
with various helpers to be written is the {3,4,0} x 1 to {3,4,5,6} portion, for example.