Max sizeof metafunction in variadic templates - c++

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

Related

Is it possible to mix SFINAE and template specialisation?

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

Why clang is not able to instantiate nested variadic template with defaulted integer_sequence?

Consider an example:
#include <utility>
template <class... Ts>
struct pack {
static constexpr std::size_t size = sizeof...(Ts);
};
template <class P, class = std::make_index_sequence<P::size>>
struct ipack;
template <class... Ts, std::size_t... Is>
struct ipack<pack<Ts...>, std::index_sequence<Is...>> {
static constexpr std::size_t size = sizeof...(Ts);
};
template <class IP, class = std::make_index_sequence<IP::size>>
struct vpack;
template <class... Ts, std::size_t... Is>
struct vpack<ipack<pack<Ts...>>, std::index_sequence<Is...>> {
static constexpr std::size_t size = sizeof...(Ts);
};
int main() {
vpack<ipack<pack<int, int, int>>> vp;
static_cast<void>(vp);
}
clang reports a problem with:
prog.cc:29:39: error: implicit instantiation of undefined template 'vpack<ipack<pack<int, int, int>, std::__1::integer_sequence<unsigned long, 0, 1, 2> >, std::__1::integer_sequence<unsigned long, 0, 1, 2>
vpack<ipack<pack<int, int, int>>> vp;
^
gcc does not share clangs feelings here. Which compiler is right? Is the code above ill-formed in some way?
I cannot reproduce your error using godbolt. Clang and gcc compile it just fine.
However, playing around with the compilers, I found that msvc doesn't like your code, because of the default parameter in ipack. Though, if you supply the parameter directly, it works:
template <class...Ts, std::size_t... Is>
struct vpack<ipack<pack<Ts...>,std::index_sequence<Is...>>, std::index_sequence<Is...>> {
static constexpr std::size_t size = sizeof...(Ts);
};
This change fixes your clang error as well. (I couldn't figure out how to get a link in wandbox...)
Edit:
There is another error msvc is pointing out, which I omitted above. vpack has to be constructible. However, because you just declared it (struct vpack;), there is no default constructor available. You can fix this by defining it, using: struct vpack {};. That solves the clang problem as well. (Even without the above.)
Edit 2:
Thinking about why you need to use struct vpack {}; I found another flaw in your code. It can be reduced to:
#include <utility>
template <class... Ts>
struct pack {
static constexpr std::size_t size = sizeof...(Ts);
};
template <class P, class = std::make_index_sequence<P::size>>
struct ipack {};
template <class... Ts, std::size_t... Is>
struct ipack<pack<Ts...>, std::index_sequence<Is...>> {
static constexpr std::size_t size = sizeof...(Ts);
};
template <class IP, class = std::make_index_sequence<IP::size>>
struct vpack {};
int main() {
vpack<ipack<pack<int, int, int>>> vp;
static_cast<void>(vp);
}
The reason is, that your template specialization of vpack does not get instantiated, is because you are using the default argument of the primary template. (vpack<ipack<pack<int, int, int>>> only supplies one argument instead of the two needed by the specialization.)
You could even remove the template specialization for ipack, were it not for the fact, that you are implicitly using it in the vpack primary. (IP::size refers to the specialization of ipack.)
(I wrote a version doing that: https://godbolt.org/g/6Gbsvd )
Thus, msvc and wandboxes clang were right in refusing to compile your code. I'm not sure why it works under gcc and godbolts clang. It probably has to do something with the way default parameters are handled...
Interestingly, you can see the difference by defining size inside the vpack primary:
#include <utility>
template <class... Ts>
struct pack {
static constexpr std::size_t size = sizeof...(Ts);
};
template <class P, class = std::make_index_sequence<P::size>>
struct ipack;
template <class... Ts, std::size_t... Is>
struct ipack<pack<Ts...>, std::index_sequence<Is...>> {
static constexpr std::size_t size = sizeof...(Ts);
};
template <class IP, class = std::make_index_sequence<IP::size>>
struct vpack { static constexpr std::size_t size = 0; };
template <class... Ts, std::size_t... Is>
struct vpack<ipack<pack<Ts...>,std::make_index_sequence<sizeof...(Ts)>>, std::index_sequence<Is...>> {
static constexpr std::size_t size = sizeof...(Ts);
};
int main() {
vpack<ipack<pack<int, int, int>>> vp;
return decltype(vp)::size;
}
gcc and clang return 3, but msvc returns 0.

How can I get the index of a type in a variadic class template?

I have a variadic Engine template class:
template <typename ... Components> class Engine;
I'd like to assign a number to each component at compile time which is equivalent to their ordering. This would be returned when making the following call:
template <typename Component> int ordinal();
So for example if:
Engine<PositionComponent, PhysicsComponent, InputComponent> engine;
was declared, the call:
engine.ordinal<PhysicsComponent>();
would return 1 and a similar call with InputComponent instead of PhysicsComponent would return 2.
Is it possible, and if yes, how would one go about it?
In Boost.Mp11, this is a short one-liner (as always):
template <typename... Components>
struct Engine {
template <typename Component>
static constexpr int ordinal() {
return mp_find<Engine, Component>::value;
}
};
Note that if Component is absent, this will return sizeof...(Components). If desired, you can add a static assertion to verify this.
My original answer follows below the fold...
So you want to find the index of Component in Components...?
template <typename... >
struct index;
// found it
template <typename T, typename... R>
struct index<T, T, R...>
: std::integral_constant<size_t, 0>
{ };
// still looking
template <typename T, typename F, typename... R>
struct index<T, F, R...>
: std::integral_constant<size_t, 1 + index<T,R...>::value>
{ };
Usage:
template <typename Component>
size_t ordinal() { return index<Component, Components...>::value; }
As constructed, trying to get the ordinal of a Component not in Components... will be a compile error. Which seems appropriate.
My goal below is to keep things in the compile-time realm as much as possible.
This is an alias to remove some boilerplate. std::integral_constant is a wonderful std type that stores a compile-time determined integer-type:
template<std::size_t I>
using size=std::integral_constant<std::size_t, I>;
Next, a index_of type, and an index_of_t that is slightly easier to use:
template<class...>struct types{using type=types;};
template<class T, class Types>struct index_of{};
template<class T, class...Ts>
struct index_of<T, types<T, Ts...>>:size<0>{};
template<class T, class T0, class...Ts>
struct index_of<T, types<T0, Ts...>>:size<
index_of<T,types<Ts...>>::value +1
>{};
This alias returns a pure std::integral_constant, instead of a type inheriting from it:
template<class T, class...Ts>
using index_of_t = size< index_of<T, types<Ts...>>::value >;
Finally our function:
template <class Component>
static constexpr index_of_t<Component, Components...>
ordinal() const {return{};}
it is not only constexpr, it returns a value that encodes its value in its type. size<?> has a constexpr operator size_t() as well as an operator(), so you can use it in most spots that expect integer types seamlessly.
You could also use:
template<class Component>
using ordinal = index_of_t<Component, Components...>;
and now ordinal<Component> is a type representing the index of the component, instead of a function.
I'm adding this for completeness sake, it utilizes C++11's constexpr functionality, and a few stl functions. I feel it is a little cleaner than the other solutions.
//Same type
template <typename Target,typename T,typename ...Rest>
constexpr typename std::enable_if<std::is_same<Target,T>::value, size_t>
_ordinal(){
return 0;
}
//Different types
template <typename Target,typename T,typename ...Rest>
constexpr typename std::enable_if<!std::is_same<Target,T>::value, size_t>
_ordinal(){
return 1+_ordinal<Target,Rest...>();
}
UNTESTED:
template <int, typename>
constexpr int index_of() { return -1; } // type not found
template <int N, typename Component, typename Cur, typename... Components>
constexpr int index_of() {
return std::is_same<Component, Cur>::value ? N : index_of<N+1, Component, Components...>();
}
template <typename... Components>
template <typename Component>
constexpr int engine<Components...>::ordinal() {
return index_of<0, Component, Components...>();
}
I could have used structs, but I find this much cleaner (without all the ::type ugliness).
If you want a compile-time error when the type is not found, change ordinal to:
template <typename... Components>
template <typename Component>
constexpr int engine<Components...>::ordinal() {
static_assert(index_of<0, Component, Components...>()!=-1, "invalid component");
return index_of<0, Component, Components...>();
}

Select function name based on template parameter

Is there a way to automatically select between multiple non-template functions based on a template parameter?
Example:
class Aggregate
{
public:
std::string asString();
uint32_t asInt();
private:
// some conglomerate data
};
template <typename T>
T get(Aggregate& aggregate)
{
// possible map between types and functions?
return bind(aggregate, typeConvert[T])(); ??
// or
return aggregate.APPROPRIATE_TYPE_CONVERSION();
}
The solution would be nice to throw a compiler error if there is no good conversion available, i.e.
get<double>(aggregate); // compile error
I do not want to use template specialization, i.e
template<>
int get(Aggregate& aggregate)
{
return aggregate.asInt();
}
because it leads to code duplication when your get() function has more then one line of code
The pedestrian way is to define each possible option separately:
template <typename T> T get(Aggregate &); // leave undefined
template <> uint32_t get(Aggregate & a) { return a.asInt(); }
// ...
Absent any more systematic structure that encodes which function serves which conversion, I think this is the best you can do. It may be worth redefining Aggregate, though, to be more introspectible.
You may do something like (require C++11) : (https://ideone.com/UXrQFm)
template <typename T, typename... Ts> struct get_index;
template <typename T, typename... Ts>
struct get_index<T, T, Ts...> : std::integral_constant<std::size_t, 0> {};
template <typename T, typename Tail, typename... Ts>
struct get_index<T, Tail, Ts...> :
std::integral_constant<std::size_t, 1 + get_index<T, Ts...>::value> {};
template <typename T, typename Tuple> struct get_index_in_tuple;
template <typename T, typename ... Ts>
struct get_index_in_tuple<T, std::tuple<Ts...>> : get_index<T, Ts...> {};
class Aggregate
{
public:
std::string asString();
uint32_t asInt();
private:
// some conglomerate data
};
template <typename T>
T get(Aggregate& aggregate)
{
using types = std::tuple<uint32_t, std::string>;
auto funcs = std::make_tuple(&Aggregate::asInt, &Aggregate::asString);
return (aggregate.* (std::get<get_index_in_tuple<T, types>::value>(funcs)))();
}

How can I create a typedef for the first parameter of a parameter pack

How can I get access to the individual items in a parameter pack?
Given the following:
template<typename T>
struct X {};
template<class R, class... Args>
struct X<R (Args...)>
{
// how can I create a typedef for the first parameter
// basically I want to do something like if arg1 exists typedef it
// pseduo code below
if (Args[0])
typedef typename Args[0] Parameter1
}
Otherwise I might have to do something like this but was hoping to keep it generic
template<class R, class... Args>
struct X<R (Arg1, Args...)>
{
}
Getting the first item in the param pack is pretty easy with a helper:
template <typename T, typename... Ts>
using first_type = T;
However this of course will fail to compile if your parameter pack is empty. What kind of behaviour do you expect when your pack is empty? Do you just not want the typedef to exist? In that case, just partially specialize your struct X for the empty parameter pack case.
You can use std::tuple_element:
template<typename... Args>
struct arg
{
template<int N>
using type = typename std::tuple_element<N, std::tuple<Args...>>::type;
};
template<typename R, typename... Args>
struct X<R (Args...)>
{
using first = typename arg<Args...>::template type<0>;
};
That will allow you to access an arbitrary type through a compile-time index. You can also use static_assert to assert at compile-time that a parameter pack has at least one element.
template<typename... Args>
struct has_first_arg : std::true_type { };
template<>
struct has_first_arg<> : std::false_type { };
template<typename R, typename... Args>
struct X<R (Args...)>
{
static_assert(has_first_arg<Args...>::value,
"Parameter pack must have at least one element");
};