Is it possible to mix SFINAE and template specialisation? - c++

Here is what I am roughly trying to achieve:
// the declaration
template<typename... Args>
struct ArgsEstimate;
// specialisation for string, SFINAE would be overkill
template<typename... Args>
struct ArgsEstimate<std::string&, Args...> {
static const std::size_t size = 64 + ArgsEstimate<Args...>::size;
};
// specialisation for arithmetic types
template<typename AirthmeticT,
typename std::enable_if<std::is_arithmetic<AirthmeticT>::value>::type* = nullptr,
typename... Args>
struct ArgsEstimate<AirthmeticT, Args...> {
static const std::size_t size = sizeof(AirthmeticT) + ArgsEstimate<Args...>::size;
};
// specialisation for pointer types
template<typename PtrT,
typename std::enable_if<std::is_pointer<PtrT>::value>::type* = nullptr,
typename... Args>
struct ArgsEstimate<PtrT, Args...> {
static const std::size_t size = 32 + ArgsEstimate<Args...>::size;
};
The problem is that this code gives a compilation error "template parameters not deducible in partial specialization" at the points I have done enable_if. A static_assert inside the struct won't work either since there will be redefinition.
I know, I can probably do this with SFINAE and function overloading alone. However, for cases like just std::string, using SFINAE is an overkill.
So I was wondering if there is clean way of mixing template specialisation and SFINAE.

Direct answer to your question
You can, but you really can't. Your case is complicated by variadic template arguments.
// specialisation for arithmetic types
template<class AirthmeticT, class... Args>
struct ArgsEstimate<
AirthmeticT,
std::enable_if_t<std::is_arithmetic_v<AirthmeticT>>,
Args...>
{
static const std::size_t size = sizeof(AirthmeticT) + ArgsEstimate<Args...>::size;
};
This works... sort of. You just need to make sure the second parameter is always void:
ArgsEstimate<int, void, /* ... */> ok; // will use the integer specialization
ArgsEstimate<int, int, int> wrong; // oups, will use the base template.
This is impractical.
C++20 concepts
Concepts elegantly solve this:
// specialisation for arithmetic types
template<class T, class... Args>
requires std::is_arithmetic_v<T>
struct ArgsEstimate<T, Args...>
{
static const std::size_t size = sizeof(T) + ArgsEstimate<Args...>::size;
};
The pre-concepts solution
What you need to do is to split your class into two classes. One that defines the size just for 1 argument. Here you can use SFINAE. And the other one that summs them:
template <class T, class Enable = void>
struct ArgEstimate {};
// specialisation for string, SFINAE would be overkill
template<>
struct ArgEstimate<std::string&>
{
static const std::size_t size = 64;
};
// specialisation for arithmetic types
template<class T>
struct ArgEstimate<T, std::enable_if_t<std::is_arithmetic_v<T>>>
{
static const std::size_t size = sizeof(T);
};
// specialisation for pointer types
template <class T>
struct ArgEstimate<T*>
{
static const std::size_t size = 32;
};
// the declaration
template<class... Args> struct ArgsEstimate;
template<class T>
struct ArgsEstimate<T>
{
static const std::size_t size = ArgEstimate<T>::size;
};
template<class Head, class... Tail>
struct ArgsEstimate<Head, Tail...>
{
static const std::size_t size = ArgEstimate<Head>::size + ArgsEstimate<Tail...>::size;
};
And if you have C++17 you can use fold expression to simplify the sum:
template<class... Args>
struct ArgsEstimate
{
static const std::size_t size = (... + ArgEstimate<Args>::size);
};
Also just wanted to point out that you don't need SFINAE for pointers:
// specialisation for pointer types
template <class T, class... Args>
struct ArgsEstimate<T*, Args...> {
static const std::size_t size = 32 + ArgsEstimate<Args...>::size;
};

Related

Best way to enumerate the types on a tuple?

As the title says, I'm looking for the best way to enumerate the types on a tuple.
So my first attempt was the code below, which is an adaption of another answer here on stackoverflow, with the only change being that is wrapped on another class, so this doesn't require to pass the tuple type as template parameter:
#include <tuple>
using namespace std;
using uInt = size_t;
template<typename... ARGS>
class ECS_Data
{
private:
tuple<ARGS...> data;
private:
//TYPE INDEXING
template<typename T, typename Types>
struct Internal_Type_Index;
template<typename T>
struct Type_Index {
static constexpr uInt value = Internal_Type_Index<T, tuple<ARGS...>>::value;
};
template<typename T, typename U, typename... Types>
struct Internal_Type_Index<T, tuple<U, Types...>> {
static constexpr uInt value = 1 + Internal_Type_Index<T, tuple<Types...>>::value;
};
template<typename T, typename... Types>
struct Internal_Type_Index<T, tuple<T, Types...>> {
static constexpr uInt value = 0;
};
public:
template<typename T>
static constexpr uInt Type_Index_v = Type_Index<T>::value;
};
int main()
{
ECS_Data<int,float,double,long long,char> ecs_data;
return ecs_data.Type_Index_v<char>; //return 4
}
The "problem" with this is that for a tuple with 50 types let's say, it instantiate 1350 structs and each come with a static variable.
For example, for tuple<int,float,double>:
called with int would instantiate 1 struct Internal_Type_Index<int, tuple<int, Types...>> and stop because int is immediately found.
Called with float would instantiate 2 structs Internal_Type_Index<float, tuple<int, Types...>> and then Internal_Type_Index<float, tuple<float, Types...>> then stop because float is found, and so on...resulting in 3+2+1 instantiations (assuming was Type_Index_v was called for all types).
So N + (N+N^2)/2 instantiation, right? N Type_Index and (N+N^2)/2 Internal_Type_Index partial specializations (all 1350 with a static variable inside of them).
My second solution is this:
#include <tuple>
#include <utility>
using namespace std;
using uInt = size_t;
template<typename... ARGS>
class ECS_Data
{
private:
tuple<ARGS...> data;
template<typename T>
inline static uInt _index = 0;
public:
ECS_Data() {
init_index(std::make_index_sequence<sizeof...(ARGS)>());
}
template<typename T>
uInt index()const { return _index<T>; }
private:
template<size_t... N>
void init_index(std::index_sequence<N...>) { (..., (_index<ARGS> = N)); }
};
int main()
{
ECS_Data<int,float,double,char> ecs_data;
return ecs_data.index<char>();
}
Still with the example of a tuple of 50 types, this time we have:
2+N instantiations from make_index_sequence
50 static templated variables from _index
50 member function instantiations from index<T>()
This seems better number wise, but it is runtime now, because I don't see a way to make _index constexpr while initializing it with a fold expression like in the code above.
So my questions would be:
1)Do you see a way to improve the code above?
2)Considering all (for example compiler optimization magic), at the end of the day which version is better?
3)There is a yet better version you can suggest?
EDIT:Forgive if is hard to read, the text editor here on stackoverflow completely ignored my newlines.
I would do:
template <typename T, typename Tuple, std::size_t ... Is>
constexpr std::size_t tuple_index_impl(std::index_sequence<Is...>)
{
// You might adapt here to handle duplicate differently
return std::max({ std::is_same<T, std::tuple_element_t<Is, Tuple>>::value * (Is + 1)... });
}
template <typename T, typename Tuple>
constexpr std::size_t tuple_index()
{
return tuple_index_impl<T, Tuple>(std::make_index_sequence<std::tuple_size<Tuple>::value>());
}
Demo
One instantiation of both methods by type + std::index_sequence (which might be done magically by compiler in 1 unique instantiation).
I'll only take a shot at 3). My shot would look something like this:
template <typename Tuple, typename Needle, std::size_t... I>
constexpr std::size_t tuple_index_impl(std::index_sequence<I...>) {
// don't use std::disjunction_v here as it will return bool
constexpr std::size_t idx = std::disjunction<
// need I+1 to prevent a falsy result on index 0
std::integral_constant<std::size_t,
(I+1) * std::is_same_v<std::tuple_element_t<I, Tuple>, Needle>>...,
// fallthrough case
std::integral_constant<std::size_t, 0>
>::value;
static_assert(idx != 0, "type not found in tuple");
return idx - 1;
}
template <typename Tuple, typename Needle>
constexpr std::size_t tuple_index() {
return tuple_index_impl<Tuple, Needle>(
std::make_index_sequence<std::tuple_size_v<Tuple>>{});
}
Which could be used like
using T = std::tuple<int, long long, double, char, double>;
//std::cout << tuple_index<T, float>(); //ERROR: type not found in tuple
std::cout << tuple_index<T, double>(); // prints "2", the first one
std::cout << tuple_index<T, char>(); // prints "3"
Live: http://coliru.stacked-crooked.com/a/8ea39202298f2ddc
The key here is std::disjunction, which doesn't need to instantiate any template arguments after the value it has found. This will also work on any tuple-like type (things that implement the tuple_element and tuple_size interface).
Since this is obviously all constexpr/template magic, the only costs paid will be at compile time. How well this performs in that regard will depend on the efficiency of the compiler's std::tuple_element implementation, I would guess, but I haven't done any profiling.

C++ Template Type Traits Issue

I am new to making my own type traits and I want to make a type trait that allows me to identify if the passed type is the expected container.
template<typename T, typename ... Types>
struct is_array
{
static constexpr bool value = false;
};
template<typename ... Types>
struct is_array<std::array<Types...>>
{
static constexpr bool value = true;
};
I've adopted the format above and it works for all container types except for array:
constexpr bool state = is_array<std::array<int, 5>>::value;
The above evaluates to false when it should be true. This only happens for the array class and I believe it is due to it having 2 template parameters that do not have default values. I am unable to figure out a fix for this.
First, your trait should have one, and only one template parameter: the expected array type:
template<typename T/*, typename ... Types*/>
struct is_array {
static constexpr bool value = false;
};
Second, std::array is defined as:
template<
class T,
std::size_t N
> struct array;
as first type parameter T and a non-type parameter N of type std::size_t.
Therefore, your true specialization should be:
template<typename T, std::size_t N>
struct is_array<std::array<T,N>>
{
static constexpr bool value = true;
};
Note, that instead of defining a member value, we prefer to inherit from std::true_type and std::false_type which provide some other member alias + conversion operator. The code then becomes:
template<typename T>
struct is_array : std::false_type {};
template<typename T, std::size_t N>
struct is_array<std::array<T,N>> : std::true_type {};

Max sizeof metafunction in variadic templates

I'm trying to implement a metafunction(?) in variadic templates to calculate the max of sizeof of a few types at compile-time.
template<typename... Ts> struct MaxSizeof {
static constexpr size_t value = 0;
};
template<typename T, typename... Ts> struct MaxSizeof {
static constexpr size_t value = std::max(sizeof(T), typename MaxSizeof<Ts...>::value);
};
But I'm getting a few strange errors:
MaxSizeof.h(7): error C3855: 'MaxSizeof': template parameter 'Ts' is incompatible with the declaration
MaxSizeof.h(7): error C2977: 'MaxSizeof': too many template arguments
MaxSizeof.h(5): note: see declaration of 'MaxSizeof'
Could you help fixing my code?
The compiler is MSVC++2017 toolset v141.
Your specialization has not correct syntax, it should be:
template<typename T, typename... Ts>
struct MaxSizeof<T, Ts...> { // Note the <T, Ts...> here
// ....
};
std::max is only marked constexpr since C++14, so you will have to write your own. Also, you can't overload structs, which is one reason why your code fails.
Here's a solution requiring C++14's std::max, which you can change to use a custom one as required.
template<typename... Ts>
struct MaxSizeof : std::integral_constant<std::size_t, std::max({sizeof(Ts)...})> {};
There were 2 fixes needed:
As pointed out by #Phil1970 , I forgot static for value definition.
I had to specify template arguments on line 7: struct MaxSizeof<T, Ts...> { instead of simply struct MaxSizeof {.
So the following code compiles:
template<typename... Ts> struct MaxSizeof {
static constexpr size_t value = 0;
};
template<typename T, typename... Ts> struct MaxSizeof<T, Ts...> {
static constexpr size_t value = std::max(sizeof(T), typename MaxSizeof<Ts...>::value);
};
another minor fix needed:
template<typename T, typename... Ts> struct MaxSizeof<T, Ts...> {
static constexpr size_t value = std::max(sizeof(T), MaxSizeof<Ts...>::value); // there should be with no `typename`
};

Getting the number of dimensions of a std::vector/std::array

Let's say I want a class/struct type, inheriting from integral_constant<size_t, N> where N is the dimension and the dimension is achieved as follows:
template<class T>
struct dimension;
template<class T>
struct dimension<vector<T>> : integral_constant<size_t, 1> {};
template<class T>
struct dimension<vector<vector<T>>> : integral_constant<size_t, 2> {};
And then
cout << dimension<vector<int>>::value; // 1
cout << dimension<vector<vector<int>>>::value; // 2
But obviously this is not perfect, as the number of dimensions can be a infinite (in theory). Is there a way to achieve a generic solution to this?
Suggestion: I went in this direction, but no further:
template<class T, class... Tn>
struct dimension<vector<Tn...>> : integral_constant<size_t, sizeof...(Tn)> {};
Since std::vector has other template parameters this wouldn't work.
A bit hard to define "what's a container". The below checks for value_type, iterator, and const_iterator nested typedefs. Tweak the void_t check as you want. (For instance, if you want only things that can be subscripted to be recognized as containers, then add decltype(std::declval<T&>()[0]) to the list.)
Note that dimension_impl's specialization calls dimension. this allows you to specialize dimension for container-like things you don't want to be treated as a container (std::string comes to mind).
template<class T> struct dimension;
namespace details {
template<class T, class = void>
struct dimension_impl {
static constexpr std::size_t value = 0;
};
template<class T>
struct dimension_impl<T, std::void_t<typename T::value_type,
typename T::iterator,
typename T::const_iterator>> {
static constexpr std::size_t value = 1 + dimension<typename T::value_type>::value;
};
}
template<class T>
struct dimension : std::integral_constant<std::size_t,
details::dimension_impl<T>::value> {};
You can do this for a std::vector (note that the template parameter list of a std::vector is longer than 1):
template<typename T>
struct dimension { static constexpr std::size_t value = 0; };
template<typename T, typename... V>
struct dimension<std::vector<T, V...>>{
static constexpr std::size_t value = 1 + dimension<T>::value;
};
This works instead for a std::array:
template<typename>
struct dimension { static constexpr std::size_t value = 0; };
template<typename T, std::size_t N>
struct dimension<std::array<T, N>>{
static constexpr std::size_t value = 1 + dimension<T>::value;
};
It follows a minimal, working example:
#include<vector>
#include<iostream>
template<typename T>
struct dimension { static constexpr std::size_t value = 0; };
template<typename T, typename... V>
struct dimension<std::vector<T, V...>>{
static constexpr std::size_t value = 1 + dimension<T>::value;
};
int main() {
std::cout << dimension<std::vector<std::vector<int>>>::value << std::endl;
}

Find number of unique values of a parameter pack

Given a parameter pack with variadic arguments, how can one find the number of unique values in the pack. I am looking for something along the lines of
no_of_uniques<0,1,2,1,2,2>::value // should return 3
My rudimentary implementation looks something this
template <size_t ... all>
struct no_of_uniques;
// this specialisation exceeds -ftemplate-depth as it has no terminating condition
template <size_t one, size_t ... all>
struct no_of_uniques<one,all...> {
static const size_t value = no_of_uniques<one,all...>::value;
};
template <size_t one, size_t two, size_t three>
struct no_of_uniques<one,two,three> {
static const size_t value = (one==two && one==three && two==three) ? 1:
(one!=two && two==three) ? 2:
(one==two && one!=three) ? 2:
(one==three && two!=three) ? 2: 3;
};
template <size_t one, size_t two>
struct no_of_uniques<one,two> {
static const size_t value = one==two ? 1: 2;
};
template <size_t one>
struct no_of_uniques<one> {
static const size_t value = 1;
};
Here, I have specialised for up to three arguments but understandably the code grows exponentially with the number of arguments. I would like to have a meta solution for this using solely STL and no third party libraries like Boost.MPL.
A similar question albeit in the context of checking unique types, rather than finding number of unique values of parameter pack can be found here:
Check variadic templates parameters for uniqueness
In the process of finding the number of unique values of a parameter pack, we might need to sort the pack first, and an excellent implementation of that is provided in this other question
Quick sort at compilation time using C++11 variadic templates
Here's a simple O(n^2) way to do it
template <size_t...>
struct is_unique : std::integral_constant<bool, true> {};
template <size_t T, size_t U, size_t... VV>
struct is_unique<T, U, VV...> : std::integral_constant<bool, T != U && is_unique<T, VV...>::value> {};
template <size_t...>
struct no_unique : std::integral_constant<size_t, 0> {};
template <size_t T, size_t... UU>
struct no_unique<T, UU...> : std::integral_constant<size_t, is_unique<T, UU...>::value + no_unique<UU...>::value> {};
So using your example:
no_unique<0, 1, 2, 1, 2, 2>::value; // gives 3
Most of this is machinery I already wrote for a different question, stripped of the "counting" part.
A pack with a sizeof shortcut:
template<class... Ts> struct pack {
static constexpr size_t size = sizeof...(Ts);
};
Add a type to a pack of types, but only if it doesn't exist already:
template<class T, class PT> struct do_push;
template<class T, class...Ts>
struct do_push<T, pack<Ts...>>{
using type = std::conditional_t<std::disjunction_v<std::is_same<Ts, T>...>,
pack<Ts...>,
pack<T, Ts...>
>;
};
template<class T, class PT> using push = typename do_push<T, PT>::type;
Now make a pack of unique types:
template<class P, class PT = pack<> >
struct unique_types_imp { using type = PT; };
template<class PT, class T, class... Ts>
struct unique_types_imp <pack<T, Ts...>, PT>
: unique_types_imp <pack<Ts...>, push<T, PT>> {};
template<class P>
using unique_types = typename unique_types_imp<P>::type;
Finally:
template<size_t S>
using size_constant = std::integral_constant<size_t, S>;
template<size_t... all>
struct no_of_uniques{
static constexpr size_t value = unique_types<pack<size_constant<all>...>>::size;
};
Using Boost.Mp11, this is a short one-liner (as always):
template <size_t... Ns>
using no_of_uniques = mp_size<mp_unique<mp_list<mp_size_t<Ns>...>>>;
Following the same logic as described below. We lift the values into types, put them in a type list, get the unique types out of that, and then get the length.
I'll generalize to types - since metaprogramming works better in types. The algorithm for counting uniqueness is an empty argument list has 0 unique types, and a non-empty list has 1 unique type + the number of unique types in the tail of that list after having removed the initial type.
In fact, let's generalize further than that - let's write a metafunction that takes a list of types and returns the unique types in it. Once you have the unique types, it's easy to count them.
template <class... > struct typelist { };
template <class >
struct uniq;
template <class TL>
using uniq_t = typename uniq<TL>::type;
template <>
struct uniq<typelist<>> {
using type = typelist<>;
};
template <class T, class... Ts>
struct uniq<typelist<T, Ts...>>
: concat<typelist<T>, uniq_t<filter_out_t<T, typelist<Ts...>>>>
{ };
Now we just need to fill in concat and filter_out_t. The latter is basically a glorified concat anyway:
template <class... > struct concat;
template <> struct concat<> { using type = typelist<>; };
template <class... Ts>
struct concat<typelist<Ts...>> {
using type = typelist<Ts...>;
};
template <class... Ts, class... Us, class... Args>
struct concat<typelist<Ts...>, typelist<Us...>, Args...>
: concat<typelist<Ts..., Us...>, Args...>
{ };
template <class T, class TL>
struct filter_out;
template <class T, class TL>
using filter_out_t = typename filter_out<T, TL>::type;
template <class T, class... Ts>
struct filter_out<T, typelist<Ts...>>
: concat<
std::conditional_t<std::is_same<T, Ts>::value, typelist<>, typelist<Ts>>...
>
{ };
Now, given a list of types, we can figure out the unique ones. To backport to the original problem, we just need a a size metafunction:
template <size_t N>
using size_t_ = std::integral_constant<size_t, N>;
template <class > struct length;
template <class T> using length_t = typename length<T>::type;
template <class... Ts>
struct length<typelist<Ts...>>
: size_t_<sizeof...(Ts)>
{ };
And then wrap everything up in one alias:
template <size_t... Ns>
using no_of_uniques = length_t<uniq_t<typelist<size_t_<Ns>...>>>;