Avoiding struct in variadic template function - c++

Trying to search for this answer, I noticed that the titles of the questions
on variadic template functions are very uninformative when searching on
SO. You only known that the question is about variadic template. Hopefully my
question wasn't asked before and the title will help people finding it.
So, I want to make a function which has a variadic template parameter. More
precisely, as an example, let say I want to have a compile time permutation of
an array. Here is a working code:
template <typename... Ts> struct Sequence {};
template <typename T, unsigned Size, typename... SeqTis> struct Permute;
template <typename T, unsigned Size, typename... SeqTis>
struct Permute<T, Size, Sequence<SeqTis...> > {
using type = typename std::array<T, Size>;
constexpr static type permute(const type ar) {
return { (ar[SeqTis::value])... };
}
};
Then the following is perfectly legal:
using T0 = std::integral_constant<int, 0>;
using T1 = std::integral_constant<int, 1>;
using T2 = std::integral_constant<int, 2>;
using Perm120 = Permute<int, 3, Sequence<T1, T2, T0> >;
using arr3 = Perm120::type;
constexpr arr3 ar {5,7,2};
constexpr arr3 arPerm = Perm120::permute(ar);
I'm now trying to avoid using a structure so I wrote the following:
template <typename T, unsigned Size, typename... SeqTis>
constexpr typename std::array<T, Size>
permutefun<T, Size, Sequence<SeqTis...> >(const typename std::array<T, Size> ar) {
return { (ar[SeqTis::value])... };
}
And GCC refuse it saying that
essai.cpp:19:11: error: expected initializer before ‘<’ token
permutefun<T, Size, Sequence<SeqTis...> >(const typename std::array<T, Size> ar) {
^
Why is it so ?

To add to previous answers, you also need to have a convenient syntax for calling permutefun, but the current template parameters
template <typename T, unsigned Size, typename... SeqTis>
are not convenient because you'd have to call
permutefun <int, 3, T1, T2, T0>(ar);
A solution is to deduce arguments in two steps:
#include <array>
template <typename... Ts> struct Sequence {};
template <typename... SeqTis, typename T, unsigned long Size>
constexpr std::array<T, Size>
permutefun(Sequence<SeqTis...>, const std::array<T, Size> ar) {
return { (ar[SeqTis::value])... };
}
template <typename Seq, typename T, unsigned long Size>
constexpr std::array<T, Size>
permutefun(const std::array<T, Size> ar) {
return permutefun(Seq(), ar);
}
int main ()
{
using T0 = std::integral_constant<int, 0>;
using T1 = std::integral_constant<int, 1>;
using T2 = std::integral_constant<int, 2>;
using Perm120 = Sequence<T1, T2, T0>;
using arr3 = std::array <int, 3>;
constexpr arr3 ar = {5,7,2};
constexpr arr3 arPerm = permutefun <Perm120>(ar);
}
Now arguments T, Size appear last so are automatically deduced by the input array. Argument Seq comes first, but to deduce its "unpacked" parameters SeqTis... you need a call to a second overload of permutefun. This allows the convenient syntax
permutefun <Perm120>(ar);
The 2nd overload may be useful by itself, because it allows the alternative syntax
Perm120 perm;
permutefun(perm, ar);
Also note that std::array::operator[] is constexpr only in C++14. For instance, this does not compile with Clang 3.3 and -std=c++11.

Your syntax looks like an attempt at a partial function specialisation, but that's not what you want, so that's not what you should write. Since partial function specialisations aren't supported, the compiler gets confused by the unexpected <.
template <typename T, unsigned Size, typename... SeqTis>
constexpr std::array<T, Size>
permutefun(const std::array<T, Size> ar) {
// ^ removed the <...>
return { (ar[SeqTis::value])... };
}
Also, you don't need typename here, as std::array<T, Size> is already known to be a type. It's fine to leave it in, but it works just as well without it.

Do you mean:
template <typename T, unsigned Size, typename... SeqTis>
constexpr std::array<T, Size>
permutefun(const typename std::array<T, Size> ar) {
return { (ar[SeqTis::value])... };
}
partial template specialization is not possible for function.

Related

C++20 Deducing the dimensions of a multidimensional array into a parameter pack

I am implementing a container in the form of a multidimensional array. I am trying to replicate std::to_array (C++ 20), a function that creates a std::array from a one dimensional built-in array, using my multidimensional array:
template<typename T, std::size_t... N>
constexpr mdarray<std::remove_cv_t<T>, N...> to_mdarray(T (&a)[N]));
int arr[5][12]{};
to_mdarray(arr); // Returns mdarray<int, 5, 12>
I would like to deduce the dimensions of the built-in array into N and return the corresponding mdarray. I have not managed to figure out the correct syntax, I've always ended up with errors. Either I am not unfolding the pack properly, or the compiler thinks I am trying to declare a function pointer or lambda. I haven't found any resources online either.
I found two alternatives so far. One is to decay the array into a pointer and let the user specify the dimensions.
template<typename T, std::size_t... N>
constexpr mdarray<std::remove_cv_t<T>, N...> to_mdarray(T* const a);
int arr[5][12]{};
to_mdarray<5, 12>(arr);
I found the other in blog from Stanislav Arnaudov. It involves generating N-th dimensional overloads using recursive macros:
#define SIZE_T_S_1 size_t N
#define SIZE_T_S_2 SIZE_T_S_1 , size_t M
#define SIZE_T_S_3 SIZE_T_S_2 , size_t L
#define BRACKETS_S_1 [N]
#define BRACKETS_S_2 BRACKETS_S_1[M]
#define BRACKETS_S_3 BRACKETS_S_2[L]
#define LETTERS_S_1 N
#define LETTERS_S_2 LETTERS_S_1, M
#define LETTERS_S_3 LETTERS_S_2, L
#define TO_MD_N(dim) template<typename T, SIZE_T_S_##dim> \
constexpr mdarray<std::remove_cv_t<T>, LETTERS_S_##dim> to_mdarray(T (&values) BRACKETS_S_##dim);
TO_MD_N(1);
TO_MD_N(2);
TO_MD_N(3);
// Equivalent to:
// TO_MD_N(1)
template<typename T, std::size_t N>
constexpr mdarray<std::remove_cv_t<T>, N> to_mdarray(T (&values)[N]);
// TO_MD_N(2)
template<typename T, std::size_t, std::size_t M>
constexpr mdarray<std::remove_cv_t<T>, N, M> to_mdarray(T (&values)[N][M]);
// TO_MD_N(3)
template<typename T, std::size_t, std::size_t M, std::size_t L>
constexpr mdarray<std::remove_cv_t<T>, N, M, L> to_mdarray(T (&values)[N][M][L]);
Both of these solutions have flaws. One requires the user to enter the correct dimensions, the other requires defining the same function multiple times and restricting the number of dimensions.
Is there any way to do this in C++20 or is it impossible at the moment?
Unfortunately my usual trend of Boost.Mp11 one-liners will come to an end here, since Boost.Mp11 doesn't really deal with values very well and we need to convert int[5][12] into mdarray<int, 5, 12> and there's no easy mechanism to do that with that library.
Instead we just do it by hand. std::extent gives you the Nth extent and std::rank gives you the number of extents. Combine that with make_index_sequence and you can get all of 'em:
template <typename T, typename>
struct mdarray_for_impl;
template <typename A, size_t... Is>
struct mdarray_for_impl<A, std::index_sequence<Is...>> {
using type = mdarray<std::remove_all_extents_t<A>, std::extent_v<A, Is>...>;
};
template <typename T>
using mdarray_for = mdarray_for_impl<T, std::make_index_sequence<std::rank_v<T>>>::type;
Here mdarray_for<int[1][2][3]> will yield the type mdarray<int, 1, 2, 3>.
A Boost.Mp11 version anyway requires a couple helper aliases
// a type-only mdarray
template <typename T, typename... Ns>
using mdarray_t = mdarray<T, Ns::value...>;
// a type-only extent
template <typename T, typename V>
using mp_extent = mp_size_t<std::extent_v<T, V::value>>;
which then allows:
template <typename T>
using mdarray_for2 =
mp_apply<mdarray_t,
mp_append<
mp_list<std::remove_all_extents_t<T>>,
mp_transform_q<
mp_bind<mp_extent, T, _1>,
mp_iota_c<std::rank_v<T>>>
>>;
This is the same algorithm as before, except mp_iota_c<std::rank_v<T>> to get the sequence of extent indices (instead of std::make_index_sequence) and then mp_transform_q to get the nth extent (instead of using it directly in a pack expansion).
In this case for int[5][12] we build up mp_list<int, mp_size_t<5>, mp_size_t<12>> (because we're all types, no values) and then mp_apply turns that into mdarray_t<...> which turns it into mdarray<int, 5, 12>.
I would like to deduce the dimensions of the built-in array into N and return the corresponding mdarray.
Maybe... with a little of recursion...
template <std::size_t ... Ns, typename T>
constexpr mdarray<std::remove_cv_t<T>, Ns...> get_mdarray_t (T const &)
{ return {}; }
template <std::size_t ... Ns, typename T, std::size_t N>
constexpr auto get_mdarray_t (T const (&arr)[N])
{ return get_mdarray_t<Ns..., N>(arr[0]); }
you can construct a using that can be useful
template <typename T>
using mdarray_t = decltype(get_mdarray_t<>(std::declval<T>()));
The following is a full compiling C++11 example
#include <utility>
template <typename, std::size_t...>
struct mdarray
{ };
template <std::size_t ... Ns, typename T>
constexpr mdarray<std::remove_cv_t<T>, Ns...> get_mdarray_t (T const &)
{ return {}; }
template <std::size_t ... Ns, typename T, std::size_t N>
constexpr auto get_mdarray_t (T const (&arr)[N])
{ return get_mdarray_t<Ns..., N>(arr[0]); }
template <typename T>
using mdarray_t = decltype(get_mdarray_t<>(std::declval<T>()));
int main ()
{
using A0 = int;
using A1 = int[2u];
using A2 = int[2u][3u];
using A3 = int[2u][3u][5u];
using T0 = mdarray_t<A0>;
using T1 = mdarray_t<A1>;
using T2 = mdarray_t<A2>;
using T3 = mdarray_t<A3>;
using U0 = mdarray<int> ;
using U1 = mdarray<int, 2u>;
using U2 = mdarray<int, 2u, 3u>;
using U3 = mdarray<int, 2u, 3u, 5u>;
static_assert( std::is_same<T0, U0>::value, "!" );
static_assert( std::is_same<T1, U1>::value, "!" );
static_assert( std::is_same<T2, U2>::value, "!" );
static_assert( std::is_same<T3, U3>::value, "!" );
}

Creating tuple of types derived from Variadic Template Pack

Given a list of size_t values as a variadic template parameter pack, how does one make a tuple of derived types (e.g. Matrix) depending on the parameter pack in such a way, that n-th element of variadic generates Matrix<n, n+1>. For example:
make_matrix_tuple<2,3,4,5>() == make_tuple( Matrix<2,3>, Matrix<3,4>, Matrix<4,5> );
How to write make_matrix_tuple function taking in a parameter pack of size_t?
By a type derived I do not mean inheritance but dependence (?). I'm not sure what the proper term is.
Unpacking parameter pack is simple enough
template <typename ElementType, size_t... Sizes>
void make_tuple_of_vectors() { std::tuple < std::array<ElementType, Sizes> ... > tuple; }
However I believe I am a bit over my head where it comes to the next part.
I was trying recursively unpack a pair of arguments from parameter pack like so:
template <typename Type, size_t size1, size_t size2>
struct dummy_matrix
{
size_t SIZE1 = size1;
size_t SIZE2 = size2;
using type = Type;
};
template <size_t Iterator, typename ElementType, size_t T, size_t... Sizes>
struct unpack_two
{
using type = typename unpack_two<Iterator - 1, ElementType, Sizes...>::type;
};
template<typename ElementType, size_t T, size_t T2, size_t... Sizes>
struct unpack_two<0, ElementType, T, T2, Sizes...>
{
using type = dummy_matrix<ElementType, T, T2>;
};
So that unpack_two<N, Type, Sizes...>::type gives N-th and (N+1)-nth Matrix type.
With that, I'm stuck with something that appears sensible to me, yet the compiler disagrees harshly.
template <size_t... Sizes, size_t... Is>
auto
foo_impl(std::index_sequence<Is...>) {
std::tuple < unpack_two<Is, float, Sizes ... >::type ... > tuple;
return tuple;
}
template <size_t... Args>
void foo()
{
auto vs = foo_impl<Args...>(std::make_index_sequence<sizeof...(Args)-1>{});
}
int main() { foo<6,9,12>(); }
I'm trying to unpack list of std::size_t Sizes for the unpack_two template, and then unpack std::index_sequence for the std::make_tuple().
I would appreciate explanations as to why my tries are failing, or even is the std::index_sequence right tool here. But I'm mostly interested in any solution to the posed problem.
how does one make a tuple of derived types (e.g. Matrix) depending on the parameter pack in such a way, that n-th element of variadic generates Matrix<n, n+1> [?]
Maybe using a constexpr std::array in an helper function?
An example
#include <array>
#include <tuple>
#include <utility>
template <std::size_t, std::size_t>
struct Matrix
{ };
template <std::size_t ... Ds, std::size_t ... Is>
auto mmt_helper (std::index_sequence<Is...>)
{
constexpr std::array ca { Ds... };
return std::make_tuple(Matrix<ca[Is], ca[Is+1u]>{}...);
}
template <std::size_t ... Ds>
auto make_matrix_tuple ()
{ return mmt_helper<Ds...>(std::make_index_sequence<sizeof...(Ds)-1>{}); }
int main ()
{
auto mt = make_matrix_tuple<2,3,4,5>();
using T1 = decltype(mt);
using T2 = std::tuple<Matrix<2u, 3u>, Matrix<3u, 4u>, Matrix<4u, 5u>>;
static_assert( std::is_same_v<T1, T2> );
}

Initialise `std::array` with parameter pack arguments

There's structure HasArray with template parameters typename T and size_t N.
template<typename T, size_t N>
struct HasArray {
// enable_if sizeof...(T) equals N
template<typename ... Types, typename std::enable_if<sizeof...(Types) == N, int>::type = 0>
explicit HasArray(Types ... s) : arr { s... } {}
protected:
std::array<T, N> arr;
};
I would like to initialize the member array arr with the parameter-pack arguments of the constructor.
HasArray<uint32_t, 2> foo(7, 13);
But that yields a c++11-narrowing warning in Clang.
error: non-constant-expression cannot be narrowed from type 'int' to 'std::__1::array<unsigned int, 2>::value_type' (aka 'unsigned int') in initializer list [-Wc++11-narrowing]
I see no way to cast all s's of type Types to type T. Is there one?
Edit thanks for all answers. I ended up using static_cast<> on the packed parameters and SFINAE when not convertible:
template<typename T, size_t N>
struct HasArray {
// Use `static_cast` to construct `arr` with `s`
// Add `enable_if` all `Types` are `is_convertible`
template<typename ... Types,
typename std::enable_if<sizeof...(Types) == N, int>::type = 0,
typename std::enable_if<(std::is_convertible<Types, T>::value && ...), int>::type = 0>
explicit HasArray(Types ... s) : arr { static_cast<T>(s)... } {}
protected:
std::array<T, N> arr;
};
If you want to construct std::array from any value convertible to T, then the easiest solution would be just to add static_cast<T>(...) in your constructor.
template<typename T, size_t N>
struct HasArray {
template<typename ... Types,
typename std::enable_if<sizeof...(Types) == N, int>::type = 0>
explicit HasArray(Types ... s) : arr {static_cast<T>(s)... } {}
protected:
std::array<T, N> arr;
};
https://godbolt.org/z/TEoZOG
It's also possible to "SFINAE out" the constructor in case such conversion is not possible, but In my opinion the default error message would be better in current simple case and you could add static_asserts with better message in constructor body.
You might use intermediate class:
template <std::size_t, typename T>
using always_t = T;
template <typename T, typename Seq>
struct HasArrayImpl;
template <typename T, std::size_t...Is>
struct HasArrayImpl<T, std::index_sequence<Is...>>
{
explicit HasArrayImpl(always_t<Is, T> ... v) : arr { v... } {}
protected:
std::array<T, sizeof...(Is)> arr;
};
template <typename T, std::size_t N>
using HasArray = HasArrayImpl<T, std::make_index_sequence<N>>;
Else, you can extend your SFINAE to convertible type and explicitly convert values
template<typename T, size_t N>
struct HasArray {
// enable_if sizeof...(T) equals N
template <typename ... Types,
std::enable_if_t<(sizeof...(Types) == N)
&& (std::is_convertible<Types, T>::value && ...),
int>::type = 0>
explicit HasArray(Types ... s) : arr{ static_cast<T>(s)... } {}
protected:
std::array<T, N> arr;
};
I see no way to enforce that all types Types must be of type T. Is there one?
I don't understand, from your question if you want that Types are deducible template types, and all of they are deduced exactly as T, or if you want a no-template constructor that receive exaclty N values of type T.
The first case is simple (if you can use C++17 template folding; a little more complicated otherwise) because you can use std::is_same
template <typename ... Types,
typename std::enable_if<(sizeof...(Types) == N)
&& (... && std::is_same<Types, T>::value), int>::type = 0>
explicit HasArray(Types ... s) : arr {{ s... }}
{ }
For second case I propose a variation of the Jarod42 solution that uses a specialization of HasArray instead of a intermediate class (edit: added an improvement from Jarod42 itself; thanks!):
template<typename T, std::size_t N, typename = std::make_index_sequence<N>>
struct HasArray;
template<typename T, std::size_t N, std::size_t ... Is>
struct HasArray<T, N, std::index_sequence<Is...>>
{
static_assert( sizeof...(Is) == N , "wrong sequence size" );
protected:
std::array<T, N> arr;
public:
explicit HasArray(getType<T, Is> ... s) : arr {{ s... }}
{ }
};
where getType is
template <typename T, std::size_t>
using getType = T;
In first case,
HasArray<std::uint32_t, 2> foo(7, 13);
gives compilation error because 7 and 13 are deduced as int that isn't std::uin32_t.
In second case it compile becase HasArray has a constructor
HasArray(std::uint32_t, std::uint32_t)
and the ints are converted to std::uint32_t.
The following is a full compiling C++14 example for the second case
#include <array>
#include <cstdint>
#include <type_traits>
template <typename T, std::size_t>
using getType = T;
template<typename T, std::size_t N, typename = std::make_index_sequence<N>>
struct HasArray;
template<typename T, std::size_t N, std::size_t ... Is>
struct HasArray<T, N, std::index_sequence<Is...>>
{
static_assert( sizeof...(Is) == N , "wrong sequence size" );
protected:
std::array<T, N> arr;
public:
explicit HasArray(getType<T, Is> ... s) : arr {{ s... }}
{ }
};
int main ()
{
HasArray<std::uint32_t, 2> foo(7, 13);
}

How to create a tuple of fix types whose size is a known at compile time in C++17?

I would like to create a tuple type of common element type whose length is known at compile time. For example if I have
static constexpr const std::size_t compiletime_size = 2;
using tuple_int_size_2 = magic (int, compiletime_size);
tuple_int_size_2 should be the same type as std::tuple<int, int>
This can be done with recursion:
#include <tuple>
template <size_t N, typename Head, typename... T>
struct magic {
using tuple_type = typename magic<N - 1, Head, Head, T...>::tuple_type;
};
template <typename... T>
struct magic<1, T...> {
using tuple_type = std::tuple<T...>;
};
int main()
{
auto t = typename magic<3, int>::tuple_type{};
return 0;
}
I wonder, though, if std::array would be a much simpler and straight-forward solution to whatever task it is you're trying to solve.
Without recursion, with two declared (non defined) helper functions and a using
template <typename T, std::size_t ... Is>
constexpr auto gft_helper (std::index_sequence<Is...> const &)
-> decltype(std::make_tuple( ((void)Is, std::declval<T>())... ));
template <typename T, std::size_t N>
constexpr auto get_fixed_tuple ()
-> decltype(gft_helper<T>(std::make_index_sequence<N>{}));
template <typename T, std::size_t N>
using tuple_fixed_type = decltype(get_fixed_tuple<T, N>());
The following is a full working example
#include <tuple>
#include <utility>
template <typename T, std::size_t ... Is>
constexpr auto gft_helper (std::index_sequence<Is...> const &)
-> decltype(std::make_tuple( ((void)Is, std::declval<T>())... ));
template <typename T, std::size_t N>
constexpr auto get_fixed_tuple ()
-> decltype(gft_helper<T>(std::make_index_sequence<N>{}));
template <typename T, std::size_t N>
using tuple_fixed_type = decltype(get_fixed_tuple<T, N>());
int main()
{
auto ft = tuple_fixed_type<long, 3u>{};
static_assert( std::is_same<decltype(ft), std::tuple<long, long, long>>{} );
}

Variadic template for multidimensional std::array

We can alias a multidimensional array like this:
template<typename T, size_t size1, size_t size2>
using myArray = std::array<std::array<T, size2>, size1>;
But this only allows us a predefined number of dimensions. Is there a way to turn this into a variadic template, so that we could write any of those:
myArray<int, 2, 2, 2> arr3d;
myArray<int, 2, 2, 2, 2> arr4d;
I've tried a few things but wasn't completely satisfied with any of them.
This:
template<typename T, size_t size>
using myArray<T, size> = std::array<T, size>;
template<typename T, size_t size, size_t... more>
using myArray = std::array<myArray<T, more...>, size>;
doesn't even compile because template specializations are apparently not allowed with alias templates.
This is currently my best solution but deletes all constructors of std::array which I would like to keep:
template<typename T, size_t size, size_t... more>
struct myArray : public std::array<myArray<T, more...>, size> {};
template<typename T, size_t size>
struct myArray<T, size> : public std::array<T, size>{};
And with this solution I would always have to write ".internal" in front of every array access:
template<typename T, size_t size, size_t... more>
struct myArr {
std::array<myArr<T, more...>, size> internal;
};
template<typename T, size_t size>
struct myArr<T, size> {
std::array<T, size> internal;
};
So could anyone think of a solution like the second but where I could keep the constructors? I can't.
I would suggest simply multiplying the dimensions together and having a single std::array. E.g.: std::array<int, D0 * D1 * D2 * D3>. You can then provide utility functions or a wrapper class to convert a multi-dimensional index into one-dimensional.
Anyway...
Here's a simple recursive solution that uses explicit template specialization:
template <typename T, std::size_t... Ds>
struct nested_array;
template <typename T, std::size_t D>
struct nested_array<T, D>
{
using type = std::array<T, D>;
};
template <typename T, std::size_t D, std::size_t... Ds>
struct nested_array<T, D, Ds...>
{
using type = std::array<typename nested_array<T, Ds...>::type, D>;
};
static_assert(std::is_same_v<
typename nested_array<int, 1, 2, 3>::type,
std::array<std::array<std::array<int, 3>, 2>, 1>
>);
live example on wandbox
Here's a solution based on variable template partial specialization:
template <typename T>
struct t { using type = T; };
template <typename T>
using unwrap = typename T::type;
template <typename T, std::size_t... Ds>
constexpr auto nested_array = t<void>{};
template <typename T, std::size_t D>
constexpr auto nested_array<T, D> = t<std::array<T, D>>{};
template <typename T, std::size_t D, std::size_t... Ds>
constexpr auto nested_array<T, D, Ds...> =
t<std::array<unwrap<decltype(nested_array<T, Ds...>)>, D>>{};
static_assert(std::is_same_v<
unwrap<decltype(nested_array<int, 1, 2, 3>)>,
std::array<std::array<std::array<int, 3>, 2>, 1>
>);
live example on wandbox
You might use extra layer:
template<typename T, size_t size, size_t... more>
struct myArray_impl
{
using type = std::array<typename myArray_impl<T, more...>::type, size>;
};
template<typename T, size_t size>
struct myArray_impl<T, size>
{
using type = std::array<T, size>;
};
template<typename T, size_t size, size_t... more>
using myArray = typename myArray_impl<T, size, more...>::type;