Preventing conversion operators from compiling unless a static condition is met - c++

I have Vector (CVector<T, std::size_t Size>), Matrix (CMatrix<T, std::size_t Height, std::size_t Width>) and Tensor (CTensor<T, std::size_t... Sizes>) classes, and I wish to be able to implicitly convert from a CTensor class to a CVector class if sizeof...(Sizes) == 1 and to a CMatrix class if sizeof...(Sizes) == 2, so I have the following conversion operators (initially I didn't have the std::enable_if template parameter hoping I could use SFINAE to prevent it from compiling):
template <typename std::enable_if<sizeof...(Sizes) == 2, int>::type = 0>
operator CMatrix<NumType, Sizes...>() const
{
static_assert(sizeof...(Sizes) == 2, "You can only convert a rank 2 tensor to a matrix");
CMatrix<NumType, Sizes...> matResult;
auto& arrThis = m_numArray;
auto& arrResult = matResult.m_numArray;
concurrency::parallel_for_each( arrResult.extent, [=, &arrThis, &arrResult]( concurrency::index<2> index ) restrict( amp ) {
arrResult[index] = arrThis[index];
} );
return matResult;
}
template <typename std::enable_if<sizeof...(Sizes) == 1, int>::type = 0>
operator CVector<NumType, Sizes...>() const
{
static_assert(sizeof...(Sizes) == 1, "You can only convert a rank 1 tensor to a vector");
CVector<NumType, Sizes...> vecResult;
auto& arrThis = m_numArray;
auto& arrResult = vecResult.m_numArray;
concurrency::parallel_for_each( arrResult.extent, [=, &arrThis, &arrResult]( concurrency::index<1> index ) restrict( amp ) {
arrResult[index] = arrThis[index];
} );
return vecResult;
}
However, if I instantiate CTensor<float, 3, 3, 3> for instance, and try to compile, I will be presented with errors declaring that there are too many template parameters for CMatrix and CVector along with errors regarding missing type for std::enable_if<false, int>. Is there a way to implement these operators without having to specialize CTensor for rank 1 and 2?

I have simplified my previous solution, details below.
SFINAE is not needed at all because you have static_assert in template method which is instantiated only upon usage.
My solution makes the conversion operator a template method with dependant argument (so that compiler does not instantiate its body, only parses signature), and adds -1 size that pretends to be missing dimension within tensor of size 1 (not to the tensor itself, but to helper class that extracts parameter pack), to allow compiler instantiate the tensor template itself, but will not allow later on to instantiate the conversion operator within tensor of invalid dimension.
Live demo link.
#include <cstddef>
template <typename T, unsigned int index, T In, T... args>
struct GetArg
{
static const T value = GetArg<T, index-1, args...>::value;
};
template <typename T, T In, T... args>
struct GetArg<T, 0, In, args...>
{
static const T value = In;
};
template <typename T, T In>
struct GetArg<T, 1, In>
{
static const T value = -1;
};
template <typename T, std::size_t Size>
struct CVector
{
};
template <typename T, std::size_t Height, std::size_t Width>
struct CMatrix
{
};
template <typename T, std::size_t... Sizes>
struct CTensor
{
template <std::size_t SZ = sizeof...(Sizes)>
operator CVector<T, GetArg<std::size_t, 0, Sizes...>::value>() const
{
static_assert(SZ == 1, "You can only convert a rank 1 tensor to a vector");
CVector<T, Sizes...> vecResult;
return vecResult;
}
template <std::size_t SZ = sizeof...(Sizes)>
operator CMatrix<T, GetArg<std::size_t, 0, Sizes...>::value, GetArg<std::size_t, 1, Sizes...>::value>() const
{
static_assert(SZ == 2, "You can only convert a rank 2 tensor to a matrix");
CMatrix<T, Sizes...> matResult;
return matResult;
}
};
int main()
{
CTensor<float, 3> tensor3;
CTensor<float, 3, 3> tensor3_3;
CTensor<float, 3, 3, 3> tensor3_3_3;
CVector<float, 3> vec(tensor3);
//CVector<float, 3> vec2(tensor3_3); // static_assert fails!
CMatrix<float, 3, 3> mat(tensor3_3);
//CMatrix<float, 3, 3> mat2(tensor3_3_3); // static_assert fails!
}

Here is how you could do it with static_assert:
template <typename NumType,size_t... Sizes>
struct CTensor {
template<size_t n,size_t m>
operator CMatrix<NumType,n,m>() const
{
static_assert(
sizeof...(Sizes)==2,
"You can only convert a rank 2 tensor to a matrix"
);
static_assert(
std::is_same<CTensor<NumType,n,m>,CTensor>::value,
"Size mismatch"
);
...
}
template<size_t n>
operator CVector<NumType,n>() const
{
static_assert(
sizeof...(Sizes)==1,
"You can only convert a rank 1 tensor to a vector"
);
static_assert(
std::is_same<CTensor<NumType,n>,CTensor>::value,
"Size mismatch"
);
...
}
};
or with SFINAE:
template <typename NumType,size_t... Sizes>
struct CTensor {
template<size_t n,size_t m,
typename =
typename std::enable_if<
std::is_same<CTensor<NumType,n,m>,CTensor>::value, int
>::type
>
operator CMatrix<NumType,n,m>() const
{
...
}
template<size_t n,
typename =
typename std::enable_if<
std::is_same<CTensor<NumType,n>,CTensor>::value, int
>::type
>
operator CVector<NumType,n>() const
{
...
}
};
And here is another approach using function overloading:
template <typename NumType,size_t... Sizes>
struct CTensor {
template<size_t n,size_t m>
CMatrix<NumType,n,m> convert() const
{
...
}
template<size_t n>
CVector<NumType,n> convert() const
{
...
}
template <typename T>
operator T() const { return convert<Sizes...>(); }
};

This is actually longer description of my comment: Why not having CTensor only and aliasing it as CVector / CMatrix? No conversion needed, they will become the same.
...it is solving the real problem in a completely different way than the title asks for. Just for the record :)
1) Hiding base implementation in namespace detail
2) Specializing what really needs to be specialized
(this can be done by some helper struct as well - specializing the struct providing the method)
3) Aliasing CVector/CMatrix as CTensor (no need for the operator then)
#include <vector>
namespace detail {
template<class T, std::size_t... Sizes>
class base;
template<class T, std::size_t Size>
class base<T, Size> {
std::vector<T> data;
public:
T& operator[](std::size_t i) {
return data[i]; }
};
template<class T, std::size_t First, std::size_t... More>
class base<T, First, More...> {
std::vector<base<T, More...>> data;
public:
// this could be done better, just an example
base<T, More...>& operator[](std::size_t i) {
return data[i]; }
};
}
template<class T, std::size_t... Sizes>
class CTensor: public detail::base<T, Sizes...> {};
//we can specialize CTensor<T, Size>
//and CTensor<T, Width, Height> here
template<class T, std::size_t Size>
using CVector = CTensor<T, Size>;
template<class T, std::size_t Width, std::size_t Height>
using CMatrix = CTensor<T, Width, Height>;

Make sizeof...(Sizes) a dependant argument, and make the type CMatrix/CVector correct (taking the correct number of template parameter).
Using:
template <std::size_t ... Is> struct index_sequence {};
template <std::size_t I, typename T> struct index_element;
template <std::size_t I, std::size_t ... Is>
struct index_element<I, index_sequence<Is...> >
{
private:
static constexpr const std::size_t a[] = {Is...};
public:
static_assert(I < sizeof...(Is), "out of bound");
static constexpr const std::size_t value = a[I];
};
then you may do:
template <
std::size_t N = sizeof...(Sizes),
typename std::enable_if<N == 1, int>::type = 0>
operator CVector<
T,
index_element<0, index_sequence<Sizes..., 0>
>::value>() const
{
// Your implementation
}
template <
std::size_t N = sizeof...(Sizes),
typename std::enable_if<N == 2, int>::type = 0>
operator CMatrix<
T,
index_element<0, index_sequence<Sizes..., 0>>::value
index_element<1, index_sequence<Sizes..., 0, 0>>::value
>() const
{
// Your implementation
}

Related

getting a value for any type in a constexpr environment without default constructibility [duplicate]

Is there a utility in the standard library to get the index of a given type in std::variant? Or should I make one for myself? That is, I want to get the index of B in std::variant<A, B, C> and have that return 1.
There is std::variant_alternative for the opposite operation. Of course, there could be many same types on std::variant's list, so this operation is not a bijection, but it isn't a problem for me (I can have first occurrence of type on list, or unique types on std::variant list).
Update a few years later: My answer here may be a cool answer, but this is the correct one. That is how I would solve this problem today.
We could take advantage of the fact that index() almost already does the right thing.
We can't arbitrarily create instances of various types - we wouldn't know how to do it, and arbitrary types might not be literal types. But we can create instances of specific types that we know about:
template <typename> struct tag { }; // <== this one IS literal
template <typename T, typename V>
struct get_index;
template <typename T, typename... Ts>
struct get_index<T, std::variant<Ts...>>
: std::integral_constant<size_t, std::variant<tag<Ts>...>(tag<T>()).index()>
{ };
That is, to find the index of B in variant<A, B, C> we construct a variant<tag<A>, tag<B>, tag<C>> with a tag<B> and find its index.
This only works with distinct types.
I found this answer for tuple and slightly modificated it:
template<typename VariantType, typename T, std::size_t index = 0>
constexpr std::size_t variant_index() {
static_assert(std::variant_size_v<VariantType> > index, "Type not found in variant");
if constexpr (index == std::variant_size_v<VariantType>) {
return index;
} else if constexpr (std::is_same_v<std::variant_alternative_t<index, VariantType>, T>) {
return index;
} else {
return variant_index<VariantType, T, index + 1>();
}
}
It works for me, but now I'm curious how to do it in old way without constexpr if, as a structure.
You can also do this with a fold expression:
template <typename T, typename... Ts>
constexpr size_t get_index(std::variant<Ts...> const&) {
size_t r = 0;
auto test = [&](bool b){
if (!b) ++r;
return b;
};
(test(std::is_same_v<T,Ts>) || ...);
return r;
}
The fold expression stops the first time we match a type, at which point we stop incrementing r. This works even with duplicate types. If a type is not found, the size is returned. This could be easily changed to not return in this case if that's preferable, since missing return in a constexpr function is ill-formed.
If you dont want to take an instance of variant, the argument here could instead be a tag<variant<Ts...>>.
With Boost.Mp11 this is a short, one-liner:
template<typename Variant, typename T>
constexpr size_t IndexInVariant = mp_find<Variant, T>::value;
Full example:
#include <variant>
#include <boost/mp11/algorithm.hpp>
using namespace boost::mp11;
template<typename Variant, typename T>
constexpr size_t IndexInVariant = mp_find<Variant, T>::value;
int main()
{
using V = std::variant<int,double, char, double>;
static_assert(IndexInVariant<V, int> == 0);
// for duplicates first idx is returned
static_assert(IndexInVariant<V, double> == 1);
static_assert(IndexInVariant<V, char> == 2);
// not found returns ".end()"/ or size of variant
static_assert(IndexInVariant<V, float> == 4);
// beware that const and volatile and ref are not stripped
static_assert(IndexInVariant<V, int&> == 4);
static_assert(IndexInVariant<V, const int> == 4);
static_assert(IndexInVariant<V, volatile int> == 4);
}
One fun way to do this is to take your variant<Ts...> and turn it into a custom class hierarchy that all implement a particular static member function with a different result that you can query.
In other words, given variant<A, B, C>, create a hierarchy that looks like:
struct base_A {
static integral_constant<int, 0> get(tag<A>);
};
struct base_B {
static integral_constant<int, 1> get(tag<B>);
};
struct base_C {
static integral_constant<int, 2> get(tag<C>);
};
struct getter : base_A, base_B, base_C {
using base_A::get, base_B::get, base_C::get;
};
And then, decltype(getter::get(tag<T>())) is the index (or doesn't compile). Hopefully that makes sense.
In real code, the above becomes:
template <typename T> struct tag { };
template <std::size_t I, typename T>
struct base {
static std::integral_constant<size_t, I> get(tag<T>);
};
template <typename S, typename... Ts>
struct getter_impl;
template <std::size_t... Is, typename... Ts>
struct getter_impl<std::index_sequence<Is...>, Ts...>
: base<Is, Ts>...
{
using base<Is, Ts>::get...;
};
template <typename... Ts>
struct getter : getter_impl<std::index_sequence_for<Ts...>, Ts...>
{ };
And once you establish a getter, actually using it is much more straightforward:
template <typename T, typename V>
struct get_index;
template <typename T, typename... Ts>
struct get_index<T, std::variant<Ts...>>
: decltype(getter<Ts...>::get(tag<T>()))
{ };
That only works in the case where the types are distinct. If you need it to work with independent types, then the best you can do is probably a linear search?
template <typename T, typename>
struct get_index;
template <size_t I, typename... Ts>
struct get_index_impl
{ };
template <size_t I, typename T, typename... Ts>
struct get_index_impl<I, T, T, Ts...>
: std::integral_constant<size_t, I>
{ };
template <size_t I, typename T, typename U, typename... Ts>
struct get_index_impl<I, T, U, Ts...>
: get_index_impl<I+1, T, Ts...>
{ };
template <typename T, typename... Ts>
struct get_index<T, std::variant<Ts...>>
: get_index_impl<0, T, Ts...>
{ };
My two cents solutions:
template <typename T, typename... Ts>
constexpr std::size_t variant_index_impl(std::variant<Ts...>**)
{
std::size_t i = 0; ((!std::is_same_v<T, Ts> && ++i) && ...); return i;
}
template <typename T, typename V>
constexpr std::size_t variant_index_v = variant_index_impl<T>(static_cast<V**>(nullptr));
template <typename T, typename V, std::size_t... Is>
constexpr std::size_t variant_index_impl(std::index_sequence<Is...>)
{
return ((std::is_same_v<T, std::variant_alternative_t<Is, V>> * Is) + ...);
}
template <typename T, typename V>
constexpr std::size_t variant_index_v = variant_index_impl<T, V>(std::make_index_sequence<std::variant_size_v<V>>{});
If you wish a hard error on lookups of not containing type or duplicate type - here are static asserts:
constexpr auto occurrences = (std::is_same_v<T, Ts> + ...);
static_assert(occurrences != 0, "The variant cannot have the type");
static_assert(occurrences <= 1, "The variant has duplicates of the type");
Another take on it:
#include <type_traits>
namespace detail {
struct count_index {
std::size_t value = 0;
bool found = false;
template <typename T, typename U>
constexpr count_index operator+(const std::is_same<T, U> &rhs)
{
if (found)
return *this;
return { value + !rhs, rhs};
}
};
}
template <typename Seq, typename T>
struct index_of;
template <template <typename...> typename Seq, typename... Ts, typename T>
struct index_of<Seq<Ts...>, T>: std::integral_constant<std::size_t, (detail::count_index{} + ... + std::is_same<T, Ts>{}).value> {
static_assert(index_of::value < sizeof...(Ts), "Sequence doesn't contain the type");
};
And then:
#include <variant>
struct A{};
struct B{};
struct C{};
using V = std::variant<A, B, C>;
static_assert(index_of<V, B>::value == 1);
Or:
static_assert(index_of<std::tuple<int, float, bool>, float>::value == 1);
See on godbolt: https://godbolt.org/z/7ob6veWGr

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);
}

A class with an array member of generic rank

Consider the following code sample:
#include <iostream>
#include <tuple>
template<typename T, std::size_t Rank, std::size_t... In>
struct help;
template<typename T, std::size_t... In>
struct help<T, 1, In...>
{
static constexpr auto a = std::make_tuple(In...);
T data[std::get<0>(a)];
};
template<typename T, std::size_t... In>
struct help<T, 2, In...>
{
static constexpr auto a = std::make_tuple(In...);
T data[std::get<0>(a)][std::get<1>(a)];
};
template<typename T, std::size_t... In>
class foo : public help<T, sizeof...(In), In...>
{
private:
using base = help<T, sizeof...(In), In...>;
public:
template<typename... Tn>
constexpr foo(Tn&&... args)
: base{ { args... } } // constructor would only work if rank == 1
{}
T operator[](std::size_t i) noexcept { return base::data[i]; }
constexpr T operator[](std::size_t i) const noexcept { return base::data[i]; }
};
int main()
{
foo<int, 6> a = { 1, 2, 3, 4, 5, 6 };
for (std::size_t i = 0; i < 6; ++i) {
std::cout << a[i] << " ";
}
}
This is as far as I got.
I'm trying to make a class, objects of which shall be constructed as foo<int, 2>; foo<int, 3, 4>; foo<int, 1, 2, 3, 4>; These objects will hold an array member respectively of type int[2]; int[3][4]; int[1][2][3][4].
My initial thinking was to make a helper template class which would specialize quite a lot (at least until an array of rank 20). That is obviously verbose, and after I get to an array of rank 2, I have no idea what the constructor for foo should be.
The constructor problem can be solved by making the class a POD (no access specifiers, etc) and construct via aggregate initialization (much like std::array does), but that goes against my work so far with the "proper inherited specialization".
I would appreciate some ideas. How would you go about doing something like this?
I think you're overcomplicating things. Just make a type trait that gives you the correct std::array type:
template <class T, size_t... Is>
struct multi_array;
template <class T, size_t... Is>
using multi_array_t = typename multi_array<T, Is...>::type;
With zero dimensions, you just get the type:
template <class T>
struct multi_array<T> {
using type = T;
};
and with at least one dimension, you just wrap it in std::array and recurse:
template <class T, size_t I, size_t... Is>
struct multi_array<T, I, Is...> {
using type = std::array<multi_array_t<T, Is...>, I>;
};
So you get something like:
static_assert(std::is_same<multi_array_t<int, 2>, std::array<int, 2>>::value, "!");
static_assert(std::is_same<multi_array_t<int, 3, 4>, std::array<std::array<int, 4>, 3> >::value, "!");

Get index of a tuple element's type?

If I have a tuple with different element types like
std::tuple<T0, T1, T2, ...>
And how to get the index of a element type?
template<class T, class Tuple>
struct Index
{
enum {value = ?;}
};
Thanks.
template <class T, class Tuple>
struct Index;
template <class T, class... Types>
struct Index<T, std::tuple<T, Types...>> {
static const std::size_t value = 0;
};
template <class T, class U, class... Types>
struct Index<T, std::tuple<U, Types...>> {
static const std::size_t value = 1 + Index<T, std::tuple<Types...>>::value;
};
See it live at Coliru.
This implementation returns the index of the first occurrence of a given type. Asking for the index of a type that is not in the tuple results in a compile error (and a fairly ugly one at that).
template< size_t I, typename T, typename Tuple_t>
constexpr size_t index_in_tuple_fn(){
static_assert(I < std::tuple_size<Tuple_t>::value,"The element is not in the tuple");
typedef typename std::tuple_element<I,Tuple_t>::type el;
if constexpr(std::is_same<T,el>::value ){
return I;
}else{
return index_in_tuple_fn<I+1,T,Tuple_t>();
}
}
template<typename T, typename Tuple_t>
struct index_in_tuple{
static constexpr size_t value = index_in_tuple_fn<0,T,Tuple_t>();
};
The example above avoids generating tons of sub tuples, which makes compilation fail (out of memory) when you call index_in_tuple for large tuples
With constexpr "function" (or lambda), you might do
template <class T, class Tuple>
struct Index;
template <class T, typename... Ts>
struct Index<T, std::tuple<Ts...>>
{
static constexpr std::size_t index = [](){
constexpr std::array<bool, sizeof...(Ts)> a{{ std::is_same<T, Ts>::value... }};
// You might easily handle duplicate index too (take the last, throw, ...)
// Here, we select the first one.
const auto it = std::find(a.begin(), a.end(), true);
// You might choose other options for not present.
// As we are in constant expression, we will have compilation error.
// and not a runtime expection :-)
if (it == a.end()) throw std::runtime_error("Not present");
return std::distance(a.begin(), it);
}();
};
Actually requires C++20 as missing constexpr for std functions,
but can easily be rewritten for previous version. (C++11 would be trickier with the strong restriction for constexpr).
Yet another one using fold expression.
It also sets the value to -1 when not found.
template <class X, class Tuple>
class Idx;
template <class X, class... T>
class Idx<X, std::tuple<T...>> {
template <std::size_t... idx>
static constexpr ssize_t find_idx(std::index_sequence<idx...>) {
return -1 + ((std::is_same<X, T>::value ? idx + 1 : 0) + ...);
}
public:
static constexpr ssize_t value = find_idx(std::index_sequence_for<T...>{});
};
live: https://onlinegdb.com/SJE8kOYdv
EDIT:
As suggested by #Jarod42, one may use std::max:
template <class X, class Tuple>
class Idx;
template <class X, class... T>
class Idx<X, std::tuple<T...>> {
template <std::size_t... idx>
static constexpr ssize_t find_idx(std::index_sequence<idx...>) {
return std::max({static_cast<ssize_t>(std::is_same_v<X, T> ? idx : -1)...});
}
public:
static constexpr ssize_t value = find_idx(std::index_sequence_for<T...>{});
};
template<typename X, class Tuple>
inline constexpr ssize_t Idx_v = Idx<X, Tuple>::value;
In case of duplicate type, this version returns the index of the last one.
live: https://onlinegdb.com/WenEBQs0L
template <typename T, typename U, typename... Us>
constexpr auto getIndex() {
if constexpr (is_same_v<T, U>) {
return 0;
} else {
if constexpr (sizeof...(Us)) {
return 1 + getIndex<T, Us...>();
} else {}
}
}
template <typename T, typename U, typename... Us>
constexpr auto getIndex(const tuple<U, Us...> &) {
return getIndex<T, U, Us...>();
}
usage
tuple the_tuple{'\0', 1, 2L, 3.0, "4", string{"5"}};
cout << getIndex<char>(the_tuple) << endl; // 0
cout << getIndex<double>(the_tuple) << endl; // 3
cout << getIndex<const char *>(the_tuple) << endl; // 4
cout << getIndex<string>(the_tuple) << endl; // 5
/* cout << getIndex<short>(the_tuple) << endl; // compile error */
Try this one, which reports error if the tuple is empty, T doesn't exist or not unique in the tuple:
template <template <typename ...> class TT, std::size_t I, typename ...Ts>
struct defer
{
using type = TT<I, Ts...>;
};
template <std::size_t, typename, typename>
struct tuple_index_helper;
template <std::size_t I, typename T, typename U, typename ...Vs>
struct tuple_index_helper<I, T, std::tuple<U, Vs...>>
{
static_assert(!std::is_same_v<T, U>, "Type not unique.");
static constexpr std::size_t index = tuple_index_helper<I, T, std::tuple<Vs...>>::index;
};
template <std::size_t I, typename T>
struct tuple_index_helper<I, T, std::tuple<>>
{
static constexpr std::size_t index = I;
};
template <std::size_t, typename, typename>
struct tuple_index;
template <std::size_t I, typename T, typename U, typename ...Vs>
struct tuple_index<I, T, std::tuple<U, Vs...>>
{
static constexpr std::size_t index = std::conditional_t<std::is_same_v<T, U>, defer<tuple_index_helper, I, T, std::tuple<Vs...>>, defer<tuple_index, I + 1, T, std::tuple<Vs...>>>::type::index;
};
template <std::size_t I, typename T>
struct tuple_index<I, T, std::tuple<>>
{
static_assert(!(I == 0), "Empty tuple.");
static_assert(!(I != 0), "Type not exist.");
};
template <typename T, typename U>
inline constexpr std::size_t tuple_index_v = tuple_index<0, T, U>::index;
Example:
std::tuple<int, float, const char*> t1{};
std::tuple<int, float, int> t2{};
std::tuple<> t3{};
constexpr auto idx = tuple_index_v<float, decltype(t1)>; // idx = 1
// constexpr auto idx2 = tuple_index_v<long long, decltype(t1)> // Error: Type not exist.
// constexpr auto idx3 = tuple_index_v<int, decltype(t2)> // Error: Type not unique.
// constexpr auto idx4 = tuple_index_v<int, decltype(t3)> // Error: Empty tuple.
This does what Qiang does, but it doesn't have that strange looking empty else branch.
It also makes sure that a tuple with unique types gets passed to it for good measure.
template <typename...>
inline constexpr auto is_unique = std::true_type{};
template <typename T, typename... Rest>
inline constexpr auto is_unique<T, Rest...> = std::bool_constant<(!std::is_same_v<T, Rest> && ...) && is_unique<Rest...>>{};
template <typename T, typename U, typename... Us>
constexpr auto getIndexImpl() {
if constexpr (std::is_same<T, U>::value) {
return 0;
} else {
static_assert(sizeof...(Us) > 0, "This tuple does not have that type");
return 1 + getIndexImpl<T, Us...>();
}
}
template <typename T, typename U, typename... Us>
constexpr auto getIndex(const std::tuple<U, Us...> &) {
static_assert(is_unique<U, Us...>, "getIndex should only be called on tuples with unique types.");
return getIndexImpl<T, U, Us...>();
}

split variadic template arguments

How do I split variadic template arguments in two halves? Something like:
template <int d> struct a {
std::array <int, d> p, q;
template <typename ... T> a (T ... t) : p ({half of t...}), q ({other half of t...}) {}
};
Luc's solution is clean and straightforward, but sorely lacks fun.
Because there is only one proper way to use variadic templates and it is to abuse them to do crazy overcomplicated metaprogramming stuff :)
Like this :
template <class T, size_t... Indx, class... Ts>
std::array<T, sizeof...(Indx)>
split_array_range_imp(pack_indices<Indx...> pi, Ts... ts)
{
return std::array<T, sizeof...(Indx)>{get<Indx>(ts...)...}; //TADA
}
template <class T, size_t begin, size_t end, class... Ts>
std::array<T, end - begin>
split_array_range(Ts... ts)
{
typename make_pack_indices<end, begin>::type indices;
return split_array_range_imp<T>(indices, ts...);
}
template <size_t N>
struct DoubleArray
{
std::array <int, N> p, q;
template <typename ... Ts>
DoubleArray (Ts ... ts) :
p( split_array_range<int, 0 , sizeof...(Ts) / 2 >(ts...) ),
q( split_array_range<int, sizeof...(Ts) / 2, sizeof...(Ts) >(ts...) )
{
}
};
int main()
{
DoubleArray<3> mya{1, 2, 3, 4, 5, 6};
std::cout << mya.p[0] << "\n" << mya.p[1] << "\n" << mya.p[2] << std::endl;
std::cout << mya.q[0] << "\n" << mya.q[1] << "\n" << mya.q[2] << std::endl;
}
It is quite short, except that we need to code some helper :
First we need the structure make_pack_indices, which is used to generate a range of integer at compile-time. For example make_pack_indices<5, 0>::type is actually the type pack_indices<0, 1, 2, 3, 4>
template <size_t...>
struct pack_indices {};
template <size_t Sp, class IntPack, size_t Ep>
struct make_indices_imp;
template <size_t Sp, size_t ... Indices, size_t Ep>
struct make_indices_imp<Sp, pack_indices<Indices...>, Ep>
{
typedef typename make_indices_imp<Sp+1, pack_indices<Indices..., Sp>, Ep>::type type;
};
template <size_t Ep, size_t ... Indices>
struct make_indices_imp<Ep, pack_indices<Indices...>, Ep>
{
typedef pack_indices<Indices...> type;
};
template <size_t Ep, size_t Sp = 0>
struct make_pack_indices
{
static_assert(Sp <= Ep, "__make_tuple_indices input error");
typedef typename make_indices_imp<Sp, pack_indices<>, Ep>::type type;
};
We also need a get() function, very similar to std::get for tuple, such as std::get<N>(ts...) return the Nth element of a parameters pack.
template <class R, size_t Ip, size_t Ij, class... Tp>
struct Get_impl
{
static R& dispatch(Tp...);
};
template<class R, size_t Ip, size_t Jp, class Head, class... Tp>
struct Get_impl<R, Ip, Jp, Head, Tp...>
{
static R& dispatch(Head& h, Tp&... tps)
{
return Get_impl<R, Ip, Jp + 1, Tp...>::dispatch(tps...);
}
};
template<size_t Ip, class Head, class... Tp>
struct Get_impl<Head, Ip, Ip, Head, Tp...>
{
static Head& dispatch(Head& h, Tp&... tps)
{
return h;
}
};
template <size_t Ip, class ... Tp>
typename pack_element<Ip, Tp...>::type&
get(Tp&... tps)
{
return Get_impl<typename pack_element<Ip, Tp...>::type, Ip, 0, Tp...>::dispatch(tps...);
}
But to build get() we also need a pack_element helper structure, again very similar to std::tuple_element, such as pack_element<N, Ts...>::type is the Nth type of the parameters pack.
template <size_t _Ip, class _Tp>
class pack_element_imp;
template <class ..._Tp>
struct pack_types {};
template <size_t Ip>
class pack_element_imp<Ip, pack_types<> >
{
public:
static_assert(Ip == 0, "tuple_element index out of range");
static_assert(Ip != 0, "tuple_element index out of range");
};
template <class Hp, class ...Tp>
class pack_element_imp<0, pack_types<Hp, Tp...> >
{
public:
typedef Hp type;
};
template <size_t Ip, class Hp, class ...Tp>
class pack_element_imp<Ip, pack_types<Hp, Tp...> >
{
public:
typedef typename pack_element_imp<Ip-1, pack_types<Tp...> >::type type;
};
template <size_t Ip, class ...Tp>
class pack_element
{
public:
typedef typename pack_element_imp<Ip, pack_types<Tp...> >::type type;
};
And here we go.
Actually I don't really understand why pack_element and get() are not in the standard library already. Those helpers are present for std::tuple, why not for parameters pack ?
Note : My implementation of pack_element and make_pack_indices is a direct transposition of std::tuple_element and __make_tuple_indices implementation found in libc++.
We still lack a lot of helpers to manipulate variadic parameter packs (or I am not aware of them). Until a nice Boost library brings them to us, we can still write our own.
For example, if you are willing to postpone your array's initialization to the constructor body, you can create and use a function that copies part of the parameter pack to an output iterator:
#include <array>
#include <cassert>
#include <iostream>
// Copy n values from the parameter pack to an output iterator
template < typename OutputIterator >
void copy_n( size_t n, OutputIterator )
{
assert ( n == 0 );
}
template < typename OutputIterator, typename T, typename... Args >
void copy_n( size_t n, OutputIterator out, const T & value, Args... args )
{
if ( n > 0 )
{
*out = value;
copy_n( n - 1, ++out, args... );
}
}
// Copy n values from the parameter pack to an output iterator, starting at
// the "beginth" element
template < typename OutputIterator >
void copy_range( size_t begin, size_t size, OutputIterator out )
{
assert( size == 0 );
}
template < typename OutputIterator, typename T, typename... Args >
void copy_range( size_t begin, size_t size, OutputIterator out, T value, Args... args )
{
if ( begin == 0 )
{
copy_n( size, out, value, args... );
}
else
{
copy_range( begin - 1, size, out, args... );
}
}
template < int N >
struct DoubleArray
{
std::array< int, N > p;
std::array< int, N > q;
template < typename... Args >
DoubleArray ( Args... args )
{
copy_range( 0, N, p.begin(), args... );
copy_range( N, N, q.begin(), args... );
}
};
int main()
{
DoubleArray<3> mya(1, 2, 3, 4, 5, 6);
std::cout << mya.p[0] << mya.p[2] << std::endl; // 13
std::cout << mya.q[0] << mya.q[2] << std::endl; // 46
}
As you can see, you can (not so) easily create your own algorithms to manipulate parameter packs; all is needed is a good understanding of recursion and pattern matching (as always when doing Template MetaProgramming).
Note that in this particular case, you may use std::initializer_list:
template<int... Is> struct index_sequence{};
template<int N, int... Is> struct make_index_sequence
{
typedef typename make_index_sequence<N - 1, N - 1, Is...>::type type;
};
template<int... Is> struct make_index_sequence<0, Is...>
{
typedef index_sequence<Is...> type;
};
template <int d> struct a {
std::array <int, d> p, q;
constexpr a (const std::initializer_list<int>& t) :
a(t, typename make_index_sequence<d>::type())
{}
private:
template <int... Is>
constexpr a(const std::initializer_list<int>& t, index_sequence<Is...>) :
p ({{(*(t.begin() + Is))...}}),
q ({{(*(t.begin() + d + Is))...}})
{}
};
Here is yet another solution:
#include <array>
#include <tuple>
#include <iostream>
template <int i, int o> struct cpyarr_ {
template < typename T, typename L > static void f (T const& t, L &l) {
l[i-1] = std::get<i-1+o> (t);
cpyarr_<i-1,o>::f (t,l);
}
};
template <int o> struct cpyarr_ <0,o> {
template < typename T, typename L > static void f (T const&, L&) {}
};
template <int i, int o, typename U, typename ... T> std::array < U, i > cpyarr (U u, T... t) {
std::tuple < U, T... > l { u, t... };
std::array < U, i > a;
cpyarr_<i,o>::f (l, a); // because std::copy uses call to memmov which is not optimized away (at least with g++ 4.6)
return a;
}
template <int d> struct a {
std::array <int, d> p, q;
template <typename ... T> a (T ... t) : p (cpyarr<d,0> (t...)), q (cpyarr<d,d> (t...)) {}
};
int main () {
a <5> x { 0,1,2,3,4,5,6,7,8,9 };
for (int i = 0; i < 5; i++)
std::cout << x.p[i] << " " << x.q[i] << "\n";
}
I know this question is quite old, but I found it only yesterday while looking for a solution to a very similar problem. I worked out a solution myself and ended up writing a small library which I believe does what you want. You can find a description here if you are still interested.