Hello guys (and happy new year!)
I'm writing a (not really) simple project in C++ (my first, coming from plain C). I was wondering if there is a way to simplify the definition for multiple functions having the same template pattern. I think an example would be better to explain the problem.
The context
Let's assume I have a "Set" class which represents a list of numbers, defined as
template <class T>
class Set {
static_assert(std::is_arithmetic<T>(), "Template argument must be an arithmetic type.");
T *_address;
...
}
So if I have an instance (say Set<double>) and an array U array[N], where U is another arithmetic type and N is an integer, I would like to be able to perform some operations such as assigning the values of the array to those of the Set. Thus I create the function template inside the class
template <class U, int N>
void assign(U (&s)[N]) {
static_assert(std::is_arithmetic<U>(), "Template argument must be an arithmetic type.");
errlog(E_BAD_ARRAY_SIZE, N == _size);
idx_t i = 0;
do {
_address[i] = value[i];
} while (++i < size);
}
The problem
As far as my tests went the code above works perfectly fine. However I find it REALLY REALLY ugly to see since I need the static_assert to ensure that only arithmetic types are taken as arguments (parameter U) and I need a way to be sure about the array size (parameter N). Also, I'm not done with the assign function but I need so many other functions such as add, multiply, scalar_product etc. etc.!
The first solution
I was wondering then if there is a prettier way to write this kind of class. After some work I've come up with a preprocessor directive:
#define arithmetic_v(_U_, _N_, _DECL_, ...) \
template <class U, idx_t N> _DECL_ \
{ \
static_assert(std::is_arithmetic<U>(),"Rvalue is not an arithmetic type."); \
errlog(E_BAD_ARRAY_SIZE, N == _size); \
__VA_ARGS__ \
}
thus defining my function as
arithmetic_v(U, N,
void assign(U (&value)[N]),
idx_t i = 0;
do {
_address[i] = value[i];
} while (++i < _size);
)
This is somehow cleaner but still isn't the best since I'm forced to lose the brackets wrapping the function's body (having to include the static_assert INSIDE the function itself for the template parameter U to be in scope).
The question
The solution I've found seems to work pretty well and the code is much more readable than before, but... Can't I use another construct allowing me to build an even cleaner definition of all the functions and still preserving the static_assert piece and the info about the array size? It would be really ugly to repeat the template code once for each function I need...
The thanks
I'm just trying to learn about the language, thus ANY additional information about this argument will be really appreciated. I've searched as much as I could stand but couldn't find anything (maybe I just couldn't think of the appropriate keywords to ask Google in order to find something relevant). Thanks in advance for your help, and happy new year to you all!
Gianluca
I strongly suggest against using macros, unless there is no way around them (a case I could think of is getting the line number for debugging purposes). From the Google C++ Style Guide (http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Preprocessor_Macros):
Macros mean that the code you see is not the same as the code the compiler sees. This can introduce unexpected behavior, especially since macros have global scope.
I really don't see why you consider using a static_assert ugly. There is another way to ensure that a template is specialized only for some types using SFINAE.
template <class T, class Enable = void>
class X;
template <class T>
class X<T, typename std::enable_if<std::is_integral<T>::value>::type> {
};
and you could do this even prettier using a using statement (no pun intended):
template <class T>
using enable_if_integral_t = typename std::enable_if<std::is_integral<T>::value>::type;
template <class T, class Enable = void>
class X;
template <class T>
class X<T, enable_if_integral_t<T>> {
};
And now
X<int> x; // ok, int is integral
X<float> y; // compile error
SFINAE (Substitution failure is not an error) is a feature in C++ in which you don't get an error if a template specialization fails.
template <bool Cond, class T = void> struct enable_if. The type T is enabled as member type enable_if::type if Cond is true. Otherwise, enable_if::type is not defined. So for a float type is_integral is false and enable_if::type doesn't exist, so the template specialization
template <class T>
class X<T, typename std::enable_if<std::is_integral<T>::value>::type>
fails, but the generic template is used instead
template <class T, class Enable = void>
class X;
which is declared, but not defined.
This is useful as you can have more specializations like:
template <class T>
using enable_if_integral_t = typename std::enable_if<std::is_integral<T>::value>::type;
template <class T>
using enable_if_floating_t = typename std::enable_if<std::is_floating_point<T>::value>::type;
template <class T, class Enable = void>
class X;
template <class T>
class X<T, enable_if_integral_t<T>> {
};
template <class T>
class X<T, enable_if_floating_t<T>> {
};
Hope you find this at least interesting.
Happy new year!
Edit
Where should I put the <T, enable_if_integral_t<T>> in a function definition? I can only get this done with class templates...
For a function, the enable_if::type can be the return type. For example if f returns int, you can have:
#include <type_traits>
template <class T>
typename std::enable_if<std::is_integral<T>::value, int>::type f(T a) {
return 2 * a;
}
int main() {
f(3); // OK
f(3.4); // error
return 0;
}
and with using:
#include <type_traits>
template <class T, class Return = void>
using enable_if_integral_t = typename std::enable_if<std::is_integral<T>::value, Return>::type;
template <class T>
enable_if_integral_t<T, int> f(T a) {
return 2 * a;
}
int main() {
f(3); // OK
f(3.4); // Error
return 0;
}
I don't understand why you consider the static_assert or errlog statements so ugly, and suspect it's partly unfamiliarity with the language. Still, you could easily write a function or macro (if you want to use __LINE__ inside the assign etc. function), to move them out of line, allowing usage like:
template <class U, int N>
void assign(U (&s)[N]) {
assert_array_n_numbers(s);
idx_t i = 0;
do {
_address[i] = s[i];
} while (++i < size);
}
Can't I use another construct allowing me to build an even cleaner definition of all the functions and still preserving the static_assert piece and the info about the array size? It would be really ugly to repeat the template code once for each function I need...
In terms of what's possible - though IMHO likely undesirable obfuscation - you could have your functions accept (an) argument(s) that has a templated implicit constructor from an array, verifying it's arithmetic in that constructor then verifying size in the function using it, allowing usage like:
template <typename U>
void assign(Arithmetic_Array<U>& s) {
assert_same_size(s);
idx_t i = 0;
do {
_address[i] = s[i];
} while (++i < size);
}
Implementation:
template <typename T>
class Arithmetic_Array
{
public:
template <size_t N>
Arithmetic_Array(T (&a)[N])
: p_(&a), size_(N)
{
static_assert(std::is_arithmetic<T>(),"Rvalue is not an arithmetic type.");
}
T& operator[](size_t i) { return p_[i]; }
const T& operator[](size_t i) const { return p_[i]; }
size_t size() const { return size_; }
private:
T* p_;
size_t size_;
};
Discussion
"cleaner" can be subjective. In particular, you should consider the value of "normal" non-macro using-the-intuitive-type C++ source as documentation and for maintainability. If a macro substantially simplifies many functions - and particularly if it's only used in an implementation file and not a shared header - then it's worthwhile, but if there's only marginal benefit it's not worth the obfuscation and de-localisation. All that template stuff might seem convoluted and ugly when you're new to the language, but after a while it's understood at a glance and helps readers understand what the function goes on to do.
It's also common in C++ to embrace a "duck typing" attitude to template's parametric polymorphism. That means that you can let people pass in arguments of whatever type, and if those types support the operations that the template implementation attempts on them (i.e. compile), then hopefully it'll be what the caller wants. That's one reason that it's a good idea to create types that have predictable semantic behaviour, for example - only using operator overloading when the affect is similar to the same operators on built in types or std::string.
The stricter enforcement you'd like has its place though - Bjarne Stroustrup and others have spent a lot of time working on "Concepts" which are a mechanism for enforcing expectations on types used as template parameters, and would have been a good fit for your "arithmetic types" stipulation here. I hope they'll make it into the next C++ Standard. Meanwhile, static assertions are a good way to go.
Related
I have a template class where the template parameter corresponds to the size of an array within the class.
template <typename T, size_t S>
class Example {
...
private:
T values[S];
};
This leads to an expected warning: “ISO C++ forbids zero-size array.” In my case, something like Example<uint8_t, 0> would make no sense, and I would like to prevent the code containing Example<..., 0> from compiling.
How do I express in C++ that S should be superior or equal to one?
The C++20 way is
template <typename T, size_t S> requires (S > 0)
class Example
{
// ...
};
You're really trading in one compiler diagnostic for another, but here's one approach:
template <typename T, size_t S,
typename=std::enable_if_t< (S>0) >>
Alternatively: use static_assert to get a friendlier error message:
template <typename T, size_t S>
class Example {
private:
static_assert(S>0);
T values[S];
};
You'll get a friendlier error message however this template will still participate in overload resolution, which in some edge cases may be undesirable.
T values[0]; is alread ill-formed. Thus, if you configure the compiler to not use language extensions, then it won't compile.
Or, you can use a static_assert.
If there are situations where S being zero are valid. You can add a specialization for that case.
template<typename T, size_t S>
class Example {
...
private:
T values[S];
};
template<typename T>
class Example<T, 0> {
...
private:
// Don't need an array here for this case.
};
This will keep your code compiling in situations where you have heavy templating that makes S zero.
I'm not sure if this is possible, but I would like to count the number of template arguments of any class like:
template <typename T>
class MyTemplateClass { ... };
template <typename T, typename U>
class MyTemplateClass2 { ... };
such that template_size<MyTemplateClass>() == 1 and template_size<MyTemplateClass2>() == 2. I'm a beginner to template templates, so I came up with this function which of course does not work:
template <template <typename... Ts> class T>
constexpr size_t template_size() {
return sizeof...(Ts);
}
because Ts can not be referenced. I also know that it might come to problems when handling variantic templates, but that is not the case, at least for my application.
Thx in advance
There is one...
° Introduction
Like #Yakk pointed out in his comment to my other answer (without saying it explicitly), it is not possible to 'count' the number of parameters declared by a template. It is, on the other hand, possible to 'count' the number of arguments passed to an instantiated template.
Like my other answer shows it, it is rather easy to count these arguments.
So...
If one cannot count parameters...
How would it be possible to instantiate a template without knowing the number of arguments this template is suppose to receive ???
Note
If you wonder why the word instantiate(d) has been stricken throughout this post,
you'll find its explanation in the footnote. So keep reading... ;)
° Searching Process
If one can manage somehow to try to instantiate a template with an increasing number of arguments, and then, detect when it fails using SFINAE (Substitution Failure Is Not An Error), it should be possible to find a generic solution to this problem... Don't you think ?
Obviously, if one wants to be able to also manage non-type parameters, it's dead.
But for templates having only typename parameters...
There is one...
Here are the elements with which one should be able to make it possible:
A template class declared with only typename parameters can receive any type as argument. Indeed, although there can have specializations defined for specific types,
a primary template cannot enforce the type of its arguments.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The above statement might no longer be true from C++20 concepts.
I cannot try ATM, but #Yakk seems rather confident on the subject. After his comment:
I think concepts breaks this. "a primary template cannot enforce the type of its arguments." is false.
He might be right if constraints are apply before the template instantiation. But...
By doing a quick jump to the introduction to Constraints and concepts, one can read, after the first code example:
"Violations of constraints are detected at compile time, early in the template instantiation process, which leads to easy to follow error messages."
To be confirmed...
It is perfectly possible to create a template having for sole purpose to
be instantiated with any number of arguments. For our use case here, it might contain only ints... (let's call it IPack).
It is possible to define a member template of IPack to define the Next IPack by adding an int to the arguments of the current IPack. So that one can progressively increase its number of arguments...
Here is maybe the missing piece. It is maybe something that most people don't realize.
(I mean, most of us uses it a lot with templates when, for example, the template accesses a member that one of its arguments must have, or when a trait tests for the existence of a specific overload, etc...)
But I think it might help in finding solutions sometimes to view it differently and say:
It is possible to declare an arbitrary type, built by assembling other types, for which the evaluation by the compiler can be delayed until it is effectively used.
Thus, it will be possible to inject the arguments of an IPack into another template...
Lastly, one should be able to detect if the operation succeeded with a testing trait making use of decltype and std::declval. (note: In the end, none of both have been used)
° Building Blocks
Step 1: IPack
template<typename...Ts>
struct IPack {
private:
template<typename U> struct Add1 {};
template<typename...Us> struct Add1<IPack<Us...>> { using Type = IPack<Us..., int>; };
public:
using Next = typename Add1<IPack<Ts...>>::Type;
static constexpr std::size_t Size = sizeof...(Ts);
};
using IPack0 = IPack<>;
using IPack1 = typename IPack0::Next;
using IPack2 = typename IPack1::Next;
using IPack3 = typename IPack2::Next;
constexpr std::size_t tp3Size = IPack3::Size; // 3
Now, one has a means to increase the number of arguments,
with a convenient way to retrieve the size of the IPack.
Next, one needs something to build an arbitrary type
by injecting the arguments of the IPack into another template.
Step 2: IPackInjector
An example on how the arguments of a template can be injected into another template.
It uses a template specialization to extract the arguments of an IPack,
and then, inject them into the Target.
template<typename P, template <typename...> class Target>
struct IPackInjector { using Type = void; };
template<typename...Ts, template <typename...> class Target>
struct IPackInjector<IPack<Ts...>, Target> { using Type = Target<Ts...>; };
template<typename T, typename U>
struct Victim;
template<typename P, template <typename...> class Target>
using IPInj = IPackInjector<P, Target>;
//using V1 = typename IPInj<IPack1, Victim>::Type; // error: "Too few arguments"
using V2 = typename IPInj<IPack2, Victim>::Type; // Victim<int, int>
//using V3 = typename IPInj<IPack3, Victim>::Type; // error: "Too many arguments"
Now, one has a means to inject the arguments of an IPack
into a Victim template, but, as one can see, evaluating Type
directly generates an error if the number of arguments does not
match the declaration of the Victim template...
Note
Have you noticed that the Victim template is not fully defined ?
It is not a complete type. It's only a forward declaration of a template.
The template to be tested will not need to be a complete type
for this solution to work as expected... ;)
If one wants to be able to pass this arbitrary built type to some detection trait one will have to find a way to delay its evaluation.
It turns out that the 'trick' (if one could say) is rather simple.
It is related to dependant names. You know this annoying rule
that enforces you to add ::template everytime you access a member template
of a template... In fact, this rule also enforces the compiler not to
evaluate an expression containing dependant names until it is
effectively used...
Oh I see ! ...
So, one only needs to prepare the IPackInjectors without
accessing its Type member, and then, pass it to our test trait, right ?
It could be done using something like that:
using TPI1 = IPackInjector<IPack1, Victim>; // No error
using TPI2 = IPackInjector<IPack2, Victim>; // No error
using TPI3 = IPackInjector<IPack3, Victim>; // No error
Indeed, the above example does not generate errors, and it confirms
that there is a means to prepare the types to be built and evaluate
them at later time.
Unfortunately, it won't be possible to pass these pre-configured
type builders to our test trait because one wants to use SFINAE
to detect if the arbitrary type can be instantiated or not.
And this is, once again, related to dependent name...
The SFINAE rule can be exploited to make the compiler silently
select another template (or overload) only if the substitution
of a parameter in a template is a dependant name.
In clear: Only for a parameter of the current template instantiation.
Hence, for the detection to work properly without generating
errors, the arbitrary type used for the test will have to be
built within the test trait with, at least, one of its parameters.
The result of the test will be assigned to the Success member...
Step 3: TypeTestor
template<typename T, template <typename...> class C>
struct TypeTestor {};
template<typename...Ts, template <typename...> class C>
struct TypeTestor<IPack<Ts...>, C>
{
private:
template<template <typename...> class D, typename V = D<Ts...>>
static constexpr bool Test(int) { return true; }
template<template <typename...> class D>
static constexpr bool Test(...) { return false; }
public:
static constexpr bool Success = Test<C>(42);
};
Now, and finally, one needs a machinery that will successively try
to instantiate our Victim template with an increasing number of arguments. There are a few things to pay attention to:
A template cannot be declared with no parameters, but it can:
Have only a parameter pack, or,
Have all its parameters defaulted.
If the test procedure begins by a failure, it means that the template must take more arguments. So, the testing must continue until a success, and then, continue until the first failure.
I first thought that it might make the iteration algorithm using template specializations a bit complicated... But after having thought a little about it, it turns out that the start conditions are not relevant.
One only needs to detect when the last test was true and next test will be false.
There must have a limit to the number of tests.
Indeed, a template can have a parameter pack, and such a template can receive an undetermined number of arguments...
Step 4: TemplateArity
template<template <typename...> class C, std::size_t Limit = 32>
struct TemplateArity
{
private:
template<typename P> using TST = TypeTestor<P, C>;
template<std::size_t I, typename P, bool Last, bool Next>
struct CheckNext {
using PN = typename P::Next;
static constexpr std::size_t Count = CheckNext<I - 1, PN, TST<P>::Success, TST<PN>::Success>::Count;
};
template<typename P, bool Last, bool Next>
struct CheckNext<0, P, Last, Next> { static constexpr std::size_t Count = Limit; };
template<std::size_t I, typename P>
struct CheckNext<I, P, true, false> { static constexpr std::size_t Count = (P::Size - 1); };
public:
static constexpr std::size_t Max = Limit;
static constexpr std::size_t Value = CheckNext<Max, IPack<>, false, false>::Count;
};
template<typename T = int, typename U = short, typename V = long>
struct Defaulted;
template<typename T, typename...Ts>
struct ParamPack;
constexpr std::size_t size1 = TemplateArity<Victim>::Value; // 2
constexpr std::size_t size2 = TemplateArity<Defaulted>::Value; // 3
constexpr std::size_t size3 = TemplateArity<ParamPack>::Value; // 32 -> TemplateArity<ParamPack>::Max;
° Conclusion
In the end, the algorithm to solve the problem is not that much complicated...
After having found the 'tools' with which it would be possible to do it, it only was a matter, as very often, of putting the right pieces at the right places... :P
Enjoy !
° Important Footnote
Here is the reason why the word intantiate(d) has been stricken at the places where it was used in relation to the Victim template.
The word instantiate(d) is simply not the right word...
It would have been better to use try to declare, or to alias the type of a future instantiation of the Victim template.
(which would have been extremely boring) :P
Indeed, none of the Victim templates gets ever instantiated within the code of this solution...
As a proof, it should be enough to see that all tests, made in the code above, are made only on forward declarations of templates.
And if you're still in doubt...
using A = Victim<int>; // error: "Too few arguments"
using B = Victim<int, int>; // No errors
template struct Victim<int, int>;
// ^^^^^^^^^^^^^^^^
// Warning: "Explicit instantiation has no definition"
In the end, there's a full sentence of the introduction which might be stricken, because this solution seems to demonstrate that:
It is possible to 'count' the number of parameters declared by a template...
Without instantiation of this template.
#include <utility>
#include <iostream>
template<template<class...>class>
struct ztag_t {};
template <template<class>class T>
constexpr std::size_t template_size_helper(ztag_t<T>) {
return 1;
}
template <template<class, class>class T>
constexpr std::size_t template_size_helper(ztag_t<T>) {
return 2;
}
template <typename T>
class MyTemplateClass { };
template <typename T, typename U>
class MyTemplateClass2 { };
template<template<class...>class T>
struct template_size:
std::integral_constant<
std::size_t,
template_size_helper(ztag_t<T>{})
>
{};
int main() {
std::cout << template_size<MyTemplateClass>::value << "\n";
std::cout << template_size<MyTemplateClass2>::value << "\n";
}
I know of no way without writing out the N overloads to support up to N arguments.
Live example.
Reflection will, of course, make this trivial.
° Before Reading This Post
This post does not answer to "How to get the number of parameters",
it answers to "how to get the number of arguments"...
It is let here for two reasons:
It might help someone who would have mixed up (like I did)
the meaning of parameters and arguments.
The techniques used in this post are closely related to the ones used
to produce the correct answer I've posted as a separate answer.
See my other answer for finding "the number of parameters" of a template.
The answer of Elliott looks more like what one usually does (though the primary template should be fully defined and "do something" IMHO). It uses a template specialization for when a template is passed as argument to the primary template.
Meanwhile, the answer of Elliott vanished...
So I've posted something similar to what he showed below.
(see "Generic Solution")
But, just to show you that you weren't that far from a working solution, and, because I noticed that you have used a function for your try, and, you declared this function constexpr, you could have written it like that:
Note
It is a 'fancy solution', but it works...
template <typename T> class MyTemplateClass {};
template <typename T, typename U> class MyTemplateClass2 {};
template <template <typename...> class T, typename...Ts>
constexpr const size_t template_size(T<Ts...> && v)
{
return sizeof...(Ts);
}
// If the target templates are complete and default constructible.
constexpr size_t size1 = template_size(MyTemplateClass<int>{});
constexpr size_t size2 = template_size(MyTemplateClass2<int, short>{});
// If the target templates are complete but NOT default constructible.
constexpr size_t size3 = template_size(decltype(std::declval<MyTemplateClass<int>>()){});
constexpr size_t size4 = template_size(decltype(std::declval<MyTemplateClass2<int, short>>()){});
Explanation
You said "because Ts can not be referenced", which is true and false, because of the way you made the declaration of template_size.
That is, a template template parameter cannot declare parameters itself (where you placed Ts in the declaration of the function template). It is allowed to do so to give a clue of what the template argument is expected to receive as argument, but it is not a declaration of a parameter name for the current template declaration...
(I hope it's clear enough) ;)
Obviously, it might be a little bit over complicated, but it worth knowing I think that such a construct is possible also... ;)
Generic Solution
template <typename T> class MyTemplateClass {};
template <typename T, typename U> class MyTemplateClass2 {};
template<typename T>
struct NbParams { static constexpr std::size_t Value = 0; };
template<template <typename...> class C, typename...Ts>
struct NbParams<C<Ts...>> { static constexpr std::size_t Value = sizeof...(Ts); };
constexpr size_t size1 = NbParams<MyTemplateClass<int>>::Value;
constexpr size_t size2 = NbParams<MyTemplateClass2<int, short>>::Value;
That is the regular way one does this kind of things... ;)
I have recently updated gcc compiler from version 5 to 8, and it has broken our production code. A simplified version of the broken code is included below:
#include <utility>
// Imagine this has several template parameters not just Id and
// this class provides lots of friend functions for retrieving
// all this "metadata". Just one is implemented in this example.
template <typename Tag, unsigned Id>
class MetadataImpl
{
template <typename T, typename U>
using matches =
typename std::enable_if<std::is_same<T, U>::value>::type;
template <typename _Tag, typename = matches<_Tag, Tag>>
friend unsigned GetId(Tag* = nullptr)
{ return Id; }
};
// Let's generate some instances...
template class MetadataImpl<int, 1>;
template class MetadataImpl<double, 2>;
// And a simple test function.
unsigned test()
{
return GetId<int>();
}
In simplest terms, this code provides a way of capturing metadata around a tag (a type in the example above, but could also be an enum value) and was originally coded some 10+ years ago and has seen many gcc upgrades, but something "broke" in gcc 6 (verified via the famous godbolt online compiler).
It is quite possible that this code wasn't supported by the c++ standard and was just a gcc extension which has now been dropped, but I would be interested to know if this was actually the case and what the rationale might be for it being rejected by the standard.
It seems also that clang doesn't support this code either but I have noticed that if you do an ast-dump (clang -Xclang -ast-dump) that clang does at least hold the definitions of these friend functions, but it seems it is unable to find them when used (a template argument deduction failure?).
I would be very delighted to know of any work-around or alternative that works in as similar a way as possible, i.e. though some form of single line instantiation and, critically, only for tags that have been explicitly instantiated.
Specifically, what I don't want is to have a string of template functions that all have to be implemented per tag (I've just shown one metadata item and there are many in the production code, some of which derive further information from combinations of template arguments and/or other type information). The original solution developed above led to very clean, extendable and maintainable code. Wrapping it all in some complex macro would be the absolute worst-case scenario!
There is a similar question and answer here but I can't see how to make this solution work in this scenario since the argument to the friend function is not the parent class itself, but a template argument of it.
Changing the GetId function to take MetadataImpl<...> as its argument would not be a viable solution, since then the use of the functions becomes utterly impractical. The places where the functions are called from just want to provide the tag itself.
Thank you, in advance, for any help!
The reason it worked before is because gcc has bugs. It wasn't standard C++ and most probably won't ever be. But this is
namespace
{
template<typename T>
struct flag
{
friend constexpr unsigned adl(flag<T>);
};
template <typename T, unsigned n>
class meta
{
friend constexpr unsigned adl(flag<T>)
{
return n;
}
};
template<typename T>
constexpr auto getId()
{
return adl(flag<T>{});
}
}
And you get to write the exact same thing as before
template class meta<int, 1>;
template class meta<double, 2>;
auto foo()
{
return getId<int>();
}
Note the anonymous namespace, you run afoul the ODR if you don't have it.
Why don't you just write GetId as a free function and specialize it as needed?
template <typename Tag>
unsigned GetId()
{
return /* default value */;
}
template <> unsigned GetId<int> () { return 1; }
template <> unsigned GetId<double>() { return 2; }
// ...
A regex replace can help you with transforming the class template explicit instantiations to these function template specializations. (This is one of the few circumstances under which specializing a function template would make sense.)
If you don't want a default value, just define the primary function as = delete: (C++11)
template <typename Tag>
unsigned GetId() = delete;
If you can use variable templates, (C++14) you can make the code look prettier:
template <typename Tag>
unsigned Id = /* default value */;
template <> unsigned Id<int> = 1;
template <> unsigned Id<double> = 2;
// ...
So maybe this violates your "no strings of templates" requirement but you can use a tag helper struct:
template <typename T> struct tag {};
template <> struct tag<int> {
static constexpr unsigned Id = 1;
// any more customization points here
};
template <> struct tag<double> {
static constexpr unsigned Id = 2;
};
(That would also avoid the many explicit instantiations). The metadata implementation would be:
template <typename Tag>
class MetadataImpl
{
friend unsigned GetId(MetadataImpl)
{ return Tag::Id; }
};
and now you can write a helper to ADL call GetId.
template <typename T>
unsigned GetId() {
return GetId(MetadataImpl<tag<T>>());
}
Demo.
template<typename T, size_t M, size_t K, size_t N, typename std::enable_if_t<std::is_floating_point<T>::value, T> = 0>
void fastor2d(){//...}
I copied this line of code from cpp-reference(only the std::enable_if part, i do need T and all three of the size_t's), because i would like to use this function only when floating_types are used on it ... it does not compile.
Could somebody explain to me, why, and what it even does? While i am at it, how do you call this function afterwards?
Every tutorial or question here on SO gets bombed with answers, and that is great, but to someone who does not understand jacks*** of what is happening, even those are not really helpful.(sry, if possibly slightly agitated or aggressive)
EDIT: i greatly appreciate all answers as of now, i realize that my wording might have been a bit off ... i understand what a template parameter is, and know the difference between runtime and compiletime etc, but i just cant get a good grasp of the syntax behind std::enable_if
EDIT2:
template<typename T, size_t M, size_t K, size_t N, typename = std::enable_if_t<std::is_integral<T>::value>>
void fastor2d(){
Fastor::Tensor<T,M,K> A; A.randInt();
}
This is literally the only thing i need changed. Notice the random() part
template<typename T, size_t M, size_t K, size_t N, typename = std::enable_if_t<std::is_floating_point<T>::value>>
void fastor2d(){
Fastor::Tensor<T,M,K> A; A.random();
}
I'll try to explain this as simple as possible not to go into the language details too much since you asked for it.
Template arguments are compile time arguments (they do not change during the run-time of your application). Function arguments are run-time and have a memory address.
Calling this function would look something like this:
fastor2d<Object, 1, 2, 3>();
In the <> brackets you see the compile-time arguments or more accurately the template parameters, and the function in this case takes 0 runtime arguments in the () brackets. The last compile time argument has a default argument which is used to check whether the function should compile at all (enable_if type). If you want to know more clearly what enable if does you should search for the term SFINAE, which is a template metaprogramming technique used to determine whether a function or class should exist or not.
Here is a short SFINAE example:
template<typename T, typename = std::enable_if_t<std::is_floating_point<T>::value>>
void function(T arg)
{
}
function(0.3f); //OK
function(0.0); //OK double results in std::is_floating_point<double>::value == true
function("Hello"); //Does not exist (T is not floating point)
The reason the third function call fails, is because the function does not exist. This is because the enable if caused the function not to exist when the compile-time bool that is passed in as its' template argument is false.
std::is_floating_point<std::string>::value == false
Do note that a lot of people agree that the SFINAE syntax is horrible and that a lot of SFINAE code will not be necessary anymore with the introduction of concepts and constraints in C++ 20.
Rather than a top-down approach starting with you code snippet, I'll take a bottom-up approach to explain some important details about templates and what tools and techniques are involved.
At heart, templates are a tool that let you write C++ code that applies to a range of possible types, not strictly for a fixed type. In a statically-typed language, this is firstly a great tool for reusing code without sacrificing type safety, but in C++ in particular, templates are very powerful because they can be specialized.
Every template declaration begins with the keyword template, and a list of type or non-type (i.e value) parameters. Type parameters use the special keyword typename or class, and are used to let your code work over a range of types. Non-type parameters simply use the name of an existing type, and these let you apply your code to a range of values that are known at compile-time.
A very basic templated function might look like the following:
template<typename T> // declare a template accepting a single type T
void print(T t){ // print accepts a T and returns void
std::cout << t; // we can't know what this means until the point where T is known
}
This lets us reuse code safely for a range of possible types, and we can use it as follows:
int i = 3;
double d = 3.14159;
std::string s = "Hello, world!";
print<int>(i);
print<double>(d);
print<std::string>(s);
The compiler is even smart enough to deduce the template parameter T for each of these, so you can safely get away with the following, functionally identical code:
print(i);
print(d);
print(s);
But suppose you want print to behave differently for one type. Suppose, for example, you have a custom Point2D class that needs special handling. You can do this with a template specialization:
template<> // this begins a (full) template specialization
void print<Point2D>(Point2D p){ // we are specializing the existing template print with T=Point2D
std::cout << '(' << p.x << ',' << p.y << ')';
}
Now, anytime we use print with T=Point2D, the specialization is chosen. This is really useful, for example, if the generic template just doesn't make sense for one specific type.
std::string s = "hello";
Point2D p {0.5, 2.7};
print(s); // > hello
print(p); // > (0.5,2.7)
But what if we want to specialize a template for many types at once, based on a simple condition? This is where things become a little meta. First, let's try to express a condition in a way that lets them be used inside templates. This can be a little tricky because we need compile-time answers.
The condition here will be that T is a floating point number, which is true if T=float or T=double and false otherwise. This is actually fairly simple to achieve with template specialization alone.
// the default implementation of is_floating_point<T> has a static member that is always false
template<typename T>
struct is_floating_point {
static constexpr bool value = false;
};
// the specialization is_floating_point<float> has a static member that is always true
template<>
struct is_floating_point<float> {
static constexpr bool value = true;
};
// the specialization is_floating_point<double> has a static member that is always true
template<>
struct is_floating_point<double> {
static constexpr bool value = true;
}
Now, we can query any type to see if it's a floating point number:
is_floating_point<std::string>::value == false;
is_floating_point<int>::value == false;
is_floating_point<float>::value == true;
is_floating_point<double>::value == true;
But how can we use this compile-time condition inside another template? How can we tell the compiler which template to choose when there are many possible template specializations to choose from?
This is achieved by taking advantage of a C++ rule called SFINAE, which in basic English, says, "when there are many possible template specializations to choose from, and the current one doesn't make sense*, just skip it and try the next one."
There's a list of errors, when attempting to substitute template arguments into templated code, that cause the template to be ignored without an immediate compiler error. The list is a bit long and complex.
One possible way that a template doesn't make sense is if it tries to use a type that doesn't exist.
template<typename T>
void foo(typename T::nested_type x); // SFINAE error if T does not contain nested_type
This is the exact same trick that std::enable_if uses under the hood. enable_if is a template class accepting a type T and a bool condition, and it contains a nested type type equal to T only when the condition is true. This is also pretty easy to achieve:
template<bool condition, typename T>
struct enable_if {
// no nested type!
};
template<typename T> // partial specialization for condition=true but any T
struct enable_if<true, T> {
typedef T type; // only exists when condition=true
};
Now we have a helper that we can use in place of any type. If the condition we pass is true, then we can safely use the nested type. If the condition we pass is false, then the template is no longer considered.
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, void>::type // This is the return type!
numberFunction(T t){
std::cout << "T is a floating point";
}
template<typename T>
typename std::enable_if<!std::is_floating_point<T>::value, void>::type
numberFunction(T t){
std::cout << "T is not a floating point";
}
I completely agree that std::enable_if<std::is_floating_point<T>::value, void>::type is a messy way to spell out a type. You can read it as "void if T is floating point, and otherwise stop and try the next overload"
Finally, to take apart your example:
// we are declaring a template
template<
typename T, // that accepts some type T,
size_t M, // a size_t M,
size_t K, // a size_t K,
size_t N, // a size_t N,
// and an unnamed non-type that only makes sense when T is a floating point
typename std::enable_if_t<std::is_floating_point<T>::value, T> = 0
>
void fastor2d(){//...}
Note the = 0 at the end. That's simply a default value for the final template parameter, and it lets you get away with specifying T, M, K, and N but not the fifth parameter. The enable_if used here means that you can provide other templates called fastor2d, with their own sets of conditions.
First of all, I'll rewrite your function in a working form
template <typename T, size_t M, size_t K, size_t N,
std::enable_if_t<std::is_floating_point<T>::value, int> = 0>
void fastor2d() // ..........................................^^^ int, not T
{ }
The point is that I've changed the second template argument of std::enable_if_t form T to int.
I've also removed the typename before std::enable_if_t but isn't important: the typename is implicit in the _t at the end of std::enable_if_t, introduced from C++14. In C++11 the correct form is
// C++11 version
typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0
// ^^^^^^^^ no _t ^^^^^^
But why it works?
Start from the name: SFINAE.
Is a short form for "Substitution Failure Is Not An Error".
It's a C++ rule so that when you write some thing as
template <int I, std::enable_if_t< I == 3, int> = 0>
void foo ()
{ }
and I is 3, the condition of std::enable_if_t is true so std::enable_if_t< I == 3, int> is substituted with int so foo() is enabled but when I isn't 3, the condition of std::enable_if_t if false so std::enable_if_t< I == 3, int> is not substituted so foo() isn't enabled but this ins't an error (if, through overloading, there is another foo() function, enabled, that matches the call, obviously).
So where is the problem in your code?
The problem is that std::enable_if_t is substituted, when the first template parameter is true, with the second parameter.
So if you write
std::enable_if_t<std::is_floating_point<T>::value, T> = 0
and you call
fastor2d<float, 0u, 1u, 2u>();
the std::is_floating_point<float>::value (but you can also use the shorter form std::is_floating_point_v<T> (_v and not ::value)) so the substitution take place and you get
float = 0
but, unfortunately, a template value (not type) parameter can't be of type floating point, so you get an error.
If you use int instead of T, the substitution give you
int = 0
and this is correct.
Another solution can be use the following form
typename = std::enable_if_t<std::is_floating_point<T>::value, T>
as suggested by Andreas Loanjoe, because the substitution give you
typename = float
that is a valid syntax.
But this solution has the drawback that doesn't works when you want to write two alternative functions, as in the following example
// the following solution doesn't works
template <typename T,
typename = std::enable_if_t<true == std::is_floating_point<T>::value, int>>
void foo ()
{ }
template <typename T,
typename = std::enable_if_t<false == std::is_floating_point<T>::value, int>>
void foo ()
{ }
where works the solution based on the value
// the following works
template <typename T,
std::enable_if_t<true == std::is_floating_point<T>::value, int> = 0>
void foo ()
{ }
template <typename T,
std::enable_if_t<false == std::is_floating_point<T>::value, int> = 0>
void foo ()
{ }
I'm new to writing template metaprogramming code (vs. just reading it). So I'm running afoul of some noob issues. One of which is pretty well summarized by this non-SO post called "What happened to my SFINAE?", which I will C++11-ize as this:
(Note: I gave the methods different names only to help with my error diagnosis in this "thought experiment" example. See #R.MartinhoFernandes's notes on why you wouldn't actually choose this approach in practice for non-overloads.)
#include <type_traits>
using namespace std;
template <typename T>
struct Foo {
typename enable_if<is_pointer<T>::value, void>::type
valid_if_pointer(T) const { }
typename disable_if<is_pointer<T>::value, void>::type
valid_if_not_pointer(T) const { }
};
int main(int argc, char * argv[])
{
int someInt = 1020;
Foo<int*>().valid_if_pointer(&someInt);
Foo<int>().valid_if_not_pointer(304);
return 0;
}
#Alf says what happened to the SFINAE is "It wasn't there in the first place", and gives a suggestion that compiles, but templates the functions instead of the class. That might be right for some situations, but not all. (For instance: I'm specifically trying to write a container that can hold types that may or may not be copy-constructible, and I need to flip methods on and off based on that.)
As a workaround, I gave this a shot...which appears to work correctly.
#include <type_traits>
using namespace std;
template <typename T>
struct FooPointerBase {
void valid_if_pointer(T) const { }
};
template <typename T>
struct FooNonPointerBase {
void valid_if_not_pointer(T) const { }
};
template <typename T>
struct Foo : public conditional<
is_pointer<T>::value,
FooPointerBase<T>,
FooNonPointerBase<T> >::type {
};
int main(int argc, char * argv[])
{
int someInt = 1020;
#if DEMONSTRATE_ERROR_CASES
Foo<int*>().valid_if_not_pointer(&someInt);
Foo<int>().valid_if_pointer(304);
#else
Foo<int*>().valid_if_pointer(&someInt);
Foo<int>().valid_if_not_pointer(304);
#endif
return 0;
}
But if this is not broken (is it?), it's certainly not following a good general methodology for how to turn on and off methods in a templated class based on sniffing the type for traits. Is there a better solution?
Firstly, C++11 did not carry forward boost's disable_if. So if you're going to transition boost code, you'll need to use enable_if with a negated condition (or redefine your own disable_if construct).
Secondly, for SFINAE to reach in and apply to the method level, those methods must be templates themselves. Yet your tests have to be done against those templates' parameters...so code like enable_if<is_pointer<T> will not work. You can finesse this by making some template argument (let's say X) default to be equal to T, and then throw in a static assertion that the caller has not explicitly specialized it to something else.
This means that instead of writing:
template <typename T>
struct Foo {
typename enable_if<is_pointer<T>::value, void>::type
valid_if_pointer(T) const { /* ... */ }
typename disable_if<is_pointer<T>::value, void>::type
valid_if_not_pointer(T) const { /* ... */ }
};
...you would write:
template <typename T>
struct Foo {
template <typename X=T>
typename enable_if<is_pointer<X>::value, void>::type
valid_if_pointer(T) const {
static_assert(is_same<X,T>::value, "can't explicitly specialize");
/* ... */
}
template <typename X=T>
typename enable_if<not is_pointer<X>::value, void>::type
valid_if_not_pointer(T) const {
static_assert(is_same<X,T>::value, "can't explicitly specialize");
/* ... */
}
};
Both are now templates and the enable_if uses the template parameter X, rather than T which is for the whole class. It's specifically about the substitution that happens whilst creating the candidate set for overload resolution--in your initial version there's no template substitution happening during the overload resolution.
Note that the static assert is there to preserve the intent of the original problem, and prevent someone being able to compile things like:
Foo<int>().valid_if_pointer<int*>(someInt);
The way I see it you don't want SFINAE here. SFINAE is useful to pick between different templated overloads. Basically, you use it to help the compiler pick between template <typename Pointer> void f(Pointer); and template <typename NotPointer> void f(NotPointer);.
That's not what you want here. Here, you have two functions with different names, not two overloads of the same. The compiler can already pick between template <typename Pointer> void f(Pointer); and template <typename NotPointer> void g(NotPointer);.
I'll give an example to explain why I think SFINAE is not only unnecessary, but undesirable here.
Foo<int> not_pointer;
Foo<int*> pointer;
not_pointer.valid_if_pointer(); // #1
not_pointer.valid_if_not_pointer(); // #2
pointer.valid_if_pointer(); // #3
pointer.valid_if_not_pointer(); // #4
Now, let's say you managed to get this working with SFINAE. Attempting to compile this piece of code will yield errors on lines #1 and #4. Those errors will be something along the lines of "member not found" or similar. It may even list the function as a discarded candidate in overload resolution.
Now, let's say you didn't do this with SFINAE, but with static_assert instead. Like this:
template <typename T>
struct Foo {
void valid_if_pointer(T) const {
static_assert(std::is_pointer<T>::value, "valid_if_pointer only works for pointers");
// blah blah implementation
}
void valid_if_not_pointer(T) const {
static_assert(!std::is_pointer<T>::value, "valid_if_not_pointer only works for non-pointers");
// blah blah implementation
}
};
With this you'll get errors on the same line. But you'll get extremely short and useful errors. Something people have been asking of compiler writers for years. And it's now at your doorstep :)
You get the same thing: errors on both cases, except you get a much better one without SFINAE.
Also note that, if you didn't use static_assert at all and the implementation of the functions was only valid if given pointers or non-pointers, respectively, you would still get errors on the appropriate lines, except maybe nastier ones.
TL;DR: unless you have two actual template functions with the same name, it's preferable to use static_assert instead of SFINAE.