I am having trouble getting my code to compile. clang, g++ and icpc all give different error messages,
A bit of background before getting to the question itself:
I am working now on a template class hierarchy for working with Matrices. There are template parameters for the data type (either float or double) and for "Implementation Policy" -- at present this includes regular C++ code with loops and Intel MKL versions. The following is an abridged summary (please disregard lack of forward references, etc. in this -- that is unrelated to my question):
// Matrix.h
template <typename Type, typename IP>
class Matrix : public Matrix_Base<Type, IP>;
template <typename Matrix_Type>
class Matrix_Base{
/* ... */
// Matrix / Scalar addition
template <typename T>
Matrix_Base& operator+=(const T value_) {
return Implementation<IP>::Plus_Equal(
static_cast<Matrix_Type&>(*this), value_);
/* More operators and rest of code... */
};
struct CPP;
struct MKL;
template <typename IP>
struct Implementation{
/* This struct contains static methods that do the actual operations */
The trouble that I'm having right now is related to the implementation of the Implementation class (no pun intended). I know that I can use specialization of the Implementation template class to specialize template <> struct Implementation<MKL>{/* ... */}; however, this will result in a lot of code duplication as there are a number of operators (such as matrix-scalar addition, subtraction, ... ) for which both the generic and the specialized versions use the same code.
So, instead, I thought that I could get rid of the template specialization and just use enable_if to provide different implementations for those operators which have different implementations when using MKL (or CUDA, etc.).
This has proven to me to be more challenging than I had originally expected. The first -- for operator += (T value_) works fine. I added in a check just to make sure that the parameter is reasonable (this can be eliminated if it is the source of my troubles, which I doubt).
template <class Matrix_Type, typename Type, typename enable_if<
std::is_arithmetic<Type>::value >::type* dummy = nullptr>
static Matrix_Type& Plus_Equal(Matrix_Type& matrix_, Type value_){
uint64_t total_elements = matrix_.actual_dims.first * matrix_.actual_dims.second;
//y := A + b
#pragma parallel
for (uint64_t i = 0; i < total_elements; ++i)
matrix_.Data[i] += value_;
return matrix_;
}
However, I am having a really hard time figuring out how to deal with operator *=(T value_). This is due to the fact that float and double have different implementations for MKL but not in the general case.
Here is the declaration. Note that the 3rd parameter is a dummy parameter and was my attempt at forcing function overloading, since I cannot use partial template function specialization:
template <class Matrix_Type, typename U, typename Type =
typename internal::Type_Traits< Matrix_Type>::type, typename enable_if<
std::is_arithmetic<Type>::value >::type* dummy = nullptr>
static Matrix_Type& Times_Equal(Matrix_Type& matrix_, U value_, Type dummy_ = 0.0);
Definition for general case. :
template<class IP>
template <class Matrix_Type, typename U, typename Type, typename enable_if<
std::is_arithmetic<Type>::value >::type* dummy>
Matrix_Type& Implementation<IP>::Times_Equal(Matrix_Type& matrix_, U value_, Type){
uint64_t total_elements = matrix_.actual_dims.first * matrix_.actual_dims.second;
//y := A - b
#pragma parallel
for (uint64_t i = 0; i < total_elements; ++i)
matrix_.Data[i] *= value_;
return matrix_;
}
The trouble starts when I try to implement a specialization for MKL:
template<>
template <class Matrix_Type, typename U, typename Type, typename enable_if<
std::is_arithmetic<Type>::value >::type* dummy>
Matrix_Type& Implementation<implementation::MKL>::Times_Equal(
Matrix_Type& matrix_,
U value_,
typename enable_if<std::is_same<Type,float>::value,Type>::type)
{
float value = value_;
MKL_INT total_elements = matrix_.actual_dims.first * matrix_.actual_dims.second;
MKL_INT const_one = 1;
//y := a * b
sscal(&total_elements, &value, matrix_.Data, &const_one);
return matrix_;
}
This gives me an error in clang:
_error: out-of-line definition of 'Times_Equal' does not match any declaration in 'Implementation'_
and in g++ (shortened somewhat)
_error: template-id `Times_Equal<>' for 'Matrix_Type& Implementation::Times_Equal(...)' does not match any template declaration.
The code compiles perfectly fine if I change the 3rd parameter to be Type, rather than having the enable_if. But when I do that, I cannot see how to have separate implementations for float and double.
Any help would be greatly appreciated.
I think this would be very tedious to implement using std::enable_if, as the general cases would have to be implemented with an enable_if that turned it on if it doesn't fit one of the specializations.
Specifically addressing your code, I don't think the compiler is able to deduce Type in your MKL specialization as it is hidden away in the std::enable_if, and thus this specialization would never get called.
Instead of using enable_if you could perhaps do something like this:
#include<iostream>
struct CPP {};
struct MKL {};
namespace Implementation
{
//
// general Plus_Equal
//
template<class Type, class IP>
struct Plus_Equal
{
template<class Matrix_Type>
static Matrix_Type& apply(Matrix_Type& matrix_, Type value_)
{
std::cout << " Matrix Plus Equal General Implementation " << std::endl;
// ... do general Plus_Equal ...
return matrix_;
}
};
//
// specialized Plus_Equal for MKL with Type double
//
template<>
struct Plus_Equal<double,MKL>
{
template<class Matrix_Type>
static Matrix_Type& apply(Matrix_Type& matrix_, double value_)
{
std::cout << " Matrix Plus Equal MKL double Implementation " << std::endl;
// ... do MKL/double specialized Plus_Equal ...
return matrix_;
}
};
} // namespace Implementation
template <typename Type, typename IP, typename Matrix_Type>
class Matrix_Base
{
public:
// ... matrix base implementation ...
// Matrix / Scalar addition
template <typename T>
Matrix_Base& operator+=(const T value_)
{
return Implementation::Plus_Equal<Type,IP>::apply(static_cast<Matrix_Type&>(*this), value_);
}
// ...More operators and rest of code...
};
template <typename Type, typename IP>
class Matrix : public Matrix_Base<Type, IP, Matrix<Type,IP> >
{
// ... Matrix implementation ...
};
int main()
{
Matrix<float ,MKL> f_mkl_mat;
Matrix<double,MKL> d_mkl_mat;
f_mkl_mat+=2.0; // will use general plus equal
d_mkl_mat+=2.0; // will use specialized MKL/double version
return 0;
}
Here I used class specialization instead of std::enable_if. I found that you were very inconsistent with the IP, Type, and Matrix_Type types in your examples, so I hope I use them correctly here.
As an aside in relation to the comments on std::enable_if. I would use the form
template<... , typename std::enable_if< some bool >::type* = nullptr> void func(...);
over
template<... , typename = std::enable_if< some bool >::type> void func(...);
as it enables you to do some function overloads you couldn't do with the other form.
Hope you can use it :)
EDIT 20/12-13: After re-reading my post i found that I should explicitly do CRTP (Curiously Recurring Template Pattern), which i added in the above code. I pass both Type and IP to Matrix_Base. If you find this tedious, one could instead provide a matrix traits class, from which Matrix_Base could take them out.
template<class A>
struct Matrix_Traits;
// Specialization for Matrix class
template<class Type, class IP>
struct Matrix_Traits<Matrix<Type,IP> >
{
using type = Type;
using ip = IP;
};
Then Matrix_Basewould now only take one template argument, namely the matrix class itself, and get the types from the traits class
template<class Matrix_Type>
class Matrix_Base
{
// Matrix / Scalar addition
template <typename T>
Matrix_Base& operator+=(const T value_)
{
// We now get Type and IP from Matrix_Traits
return Implementation::Plus_Equal<typename Matrix_Traits<Matrix_Type>::type
, typename Matrix_Traits<Matrix_Type>::ip
>::apply(static_cast<Matrix_Type&>(*this), value_);
}
};
Related
Consider the following structs:
//Implementations provided elsewhere
struct A { A(int i, double d, std::string s); /* ... */ };
struct B { B(double d1, double d2); /* ... */ };
I have two conversion classes whose template signatures look like:
TupleAs< A, int, double, std::string > via1 { ... };
ArrayAs< B, double, 2 > via2 { ... };
Predictably, TupleAs converts a triplet of int,double, and std::string values into an object of type A. Similarly, ArrayAs converts a pair of two double values into an object of type B. (And yes, there are reasons why I cannot call the A and B constructors directly.)
Improving the syntax
I would like to change the syntax so I can do the following:
TupleAs< A(int,double,std::string) > via1 { ... };
ArrayAs< B(double,2) > via2 { ... };
which, I think, is more descriptive of a conversion process. The TupleAs template declaration and corresponding partial specialization would look like this:
template <typename T> struct TupleAs;
template <typename T, typename ... Args>
struct TupleAs<T(Args...)> { ... };
Compiler errors
However, if I try to do something similar with the ArrayAs version:
template <typename T> struct ArrayAs;
template <typename T, typename U, unsigned N>
struct ArrayAs<T(U,N)> { ... };
I get the following errors in clang (3.6) when trying to instantiate it (ArrayAs< B(double,2)> test;):
typeAs.cpp:14:22: error: unknown type name 'N'
struct ArrayAs<T(U,N)>{
^
typeAs.cpp:14:10: warning: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used
struct ArrayAs<T(U,N)>{
^~~~~~~~~~~~~~~
typeAs.cpp:13:45: note: non-deducible template parameter 'N'
template<typename T, typename U, unsigned N>
^
The gcc error diagnostic is a little different, but I won't post it here.
I admit that my templating skills should be better than they are, and I also concede that an analogous std::function<B(double,2)> declaration clearly is nonsense. But can someone tell me why the particular syntax I'm trying to achieve is not allowed? I looked through the C++14 standard and had trouble finding the relevant portion, and I'm having trouble interpreting the clang diagnostic message.
When you specialize TupleAs:
template <typename T, typename ... Args>
struct TupleAs<T(Args...)>
You are basically overloading the notation for a function. You are specializing on a function that takes Args... and returns a T. That is a type. You may not be using that function as a function, or really ever think about it as being a type, but that is what it is.
On the other hand, here:
template <typename T, typename U, unsigned N>
struct ArrayAs<T(U,N)> { ... };
There is no such thing as a function that takes N. It could take unsigned, but it can't take a value. There is just no such reasonable thing. From your example, B(double, 2) simply does not make sense. At best, you could write something that would allow:
template <unsigned N> using size_ = std::integral_constant<size_t, N>;
ArrayAs< B(double,size_<2>) >
Or even:
ArrayAs< B(std::array<double, 2>) >
since now we're back to using types everywhere. Whether you prefer that or not is personal preference.
The key here is that types are first-class citizens when it comes to all things template metaprogramming, and values should be avoided where possible.
template <typename T> struct ArrayAs;
template <typename T, typename U, std::size_t N>
struct ArrayAs<T(std::array<U,N>)> { ... };
works, as would:
template<class T>
struct to_array;
template<class T, size_t N>
struct to_array< T[N] > { using type = std::array<T, N>; };
template<class T>
using arr = typename to_array<T>::type;
then:
ArrayAs< Bob( arr<int[3]> ) > some_var;
live example.
Sadly, directly using ArrayAs< Bob( int[3] ) > doesn't work due to how arrays in function types decay to pointers.
i need to store pointers to instanced template functions and when function cannot be instanced i would like to store pointer to empty function instead. I looked into SFINAE but i dont think it applies here.
struct StaticEntity {
double position;
};
struct DynamicEntity {
double position;
double speed;
};
class MoveSystem {
public:
template <typename T>
void update(T& entity, double dt) {
entity.position += entity.speed*dt;
}
};
typedef void (*updateEntitiesFunc)(void* system, void* entity, double dt);
template <typename S, typename E>
static void update(void* system, void* entity, double dt)
{
// here if inner function cannot be instanced i would like to skip it and do "nothing" instead
((S*)system)->update(*(E*)entity, dt);
}
int main() {
updateEntitiesFunc uf = update<MoveSystem, DynamicEntity>;
updateEntitiesFunc uf2 = update<MoveSystem, StaticEntity>;
//^ this does not compile
// gives error: 'struct StaticEntity' has no member named 'speed'
// i would like it to compile and contain pointer to empty function
return 0;
}
It probably is solvable with some template magic that i just cant figure out.
Ideally without adding complexity to both Entity and System classes.
Design motivation:
for all my entity and system types i want to create a static array of function pointers:
updateEntitiesFunc funcs[EntityTypes::getTypesCount()][SystemTypes::getTypesCount()];
And then at runtime call correct function with type-ids:
funcs[entity->getTypeId()][system->getTypeId()](&system, &entity, dt);
At runtime i will check if entity is compatible with system but it is runtime information. So all function pointers must be registered for all entity-system pairs at compile time, even though they are not compatible. This is where i wanted to create those no-op functions.
First, metaprogramming boilerplate:
namespace details {
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<template<class...>class Z, class, class...Ts>
struct can_apply:
std::false_type
{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:
std::true_type
{};
}
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z,void,Ts...>;
Now, we can detect properties:
template<class T>
using speed_t = decltype(std::declval<T>().speed);
template<class T>
using position_t = decltype(std::declval<T>().position);
template<class T>
using has_speed = can_apply<speed_t, T>;
template<class T>
using has_position = can_apply<position_t, T>;
template<class S, class E>
using update_call_t = decltype( std::declval<S>().update( std::declval<E>(), 0.0 ) );
template<class S, class E>
using has_update = can_apply< update_call_t, S, E >;
and we have three traits, has_position, has_update and has_speed that are useful.
Now we fix MoveSystem:
struct MoveSystem {
template <class T>
std::enable_if_t< has_speed<T&>{} && has_position<T&>{} >
update(T& entity, double dt) {
entity.position += entity.speed*dt;
}
};
next, we modify update:
namespace updates {
template<class S, class E>
std::enable_if_t< has_update<S,E>{} >
update(S* system, E* entity, double dt ) {
system->update(*entity, dt);
}
void update(void*, void*, double) {}
}
template<class S, class E>
void update(void* system, void* entity, double dt) {
using updates::update;
update(static_cast<S*>(system), static_cast<E*>(entity), dt );
}
to check for the .update method working with those parameters.
I ADL-enabled the code such that if the class has a friend void update( S*, E*, double ) it will also work.
This is all SFINAE work. Note that adding more properties once we have can_apply is pretty easy. Make an alias that generates a type that only works if the property is satisfied, then write a can_apply alias that converts that application to a compile-time boolean test.
As an aside, MSVC2015 is not a C++11 compiler, in that it cannot compile the above code. In MSVC you have to track down some proprietary extensions to do the equivalent of the above code. This involves writing has_position and the other traits differently. They call the failure to obey the C++11 standard in this case an inability to do "expression SFINAE".
Note that the above uses a handful of C++14 features. Replace std::enable_if_t<??> with typename std::enable_if<??>::type, replace has_position<??>{} with has_position<??>::value and similar other changes if your compiler doesn't support it.
I have the following snipped of code, which does not compile.
#include <iostream>
struct A {
void foo() {}
};
struct B : public A {
using A::foo;
};
template<typename U, U> struct helper{};
int main() {
helper<void (A::*)(), &A::foo> compiles;
helper<void (B::*)(), &B::foo> does_not_compile;
return 0;
}
It does not compile since &B::foo resolves to &A::foo, and thus it cannot match the proposed type void (B::*)(). Since this is part of a SFINAE template that I am using to check for a very specific interface (I'm forcing specific argument types and output types), I would like for this to work independently of inheritances, while keeping the check readable.
What I tried includes:
Casting the second part of the argument:
helper<void (B::*)(), (void (B::*)())&B::foo> does_not_compile;
This unfortunately does not help as the second part is now not recognized as a constant expression, and fails.
I've tried assigning the reference to a variable, in order to check that.
constexpr void (B::* p)() = &B::foo;
helper<void (B::* const)(), p> half_compiles;
This code is accepted by clang 3.4, but g++ 4.8.1 rejects it, and I have no idea on who's right.
Any ideas?
EDIT: Since many comments are asking for a more specific version of the problem, I'll write it here:
What I'm looking for is a way to explicitly check that a class respects a specific interface. This check will be used to verify input arguments in templated functions, so that they respect the contract that those functions require, so that compilation stops beforehand in case the class and a function are not compatible (i.e. type traits kind of checking).
Thus, I need to be able to verify return type, argument type and number, constness and so on of each member function that I request. The initial question was the checking part of the bigger template that I'm using to verify matches.
A working solution to your problem as posted at https://ideone.com/mxIVw3 is given below - see also live example.
This problem is in a sense a follow-up of Deduce parent class of inherited method in C++. In my answer, I defined a type trait member_class that extracts a class from a given pointer to member function type. Below we use some more traits to analyse and then synthesize back such a type.
First, member_type extracts the signature, e.g. void (C::*)() gives void():
template <typename M> struct member_type_t { };
template <typename M> using member_type = typename member_type_t <M>::type;
template <typename T, typename C>
struct member_type_t <T C::*> { using type = T;};
Then, member_class extracts the class, e.g. void (C::*)() gives C:
template<typename>
struct member_class_t;
template<typename M>
using member_class = typename member_class_t <M>::type;
template<typename R, typename C, typename... A>
struct member_class_t <R(C::*)(A...)> { using type = C; };
template<typename R, typename C, typename... A>
struct member_class_t <R(C::*)(A...) const> { using type = C const; };
// ...other qualifier specializations
Finally, member_ptr synthesizes a pointer to member function type given a class and a signature, e.g. C + void() give void (C::*)():
template <typename C, typename S>
struct member_ptr_t;
template <typename C, typename S>
using member_ptr = typename member_ptr_t <C, S>::type;
template <typename C, typename R, typename ...A>
struct member_ptr_t <C, R(A...)> { using type = R (C::*)(A...); };
template <typename C, typename R, typename ...A>
struct member_ptr_t <C const, R(A...)> { using type = R (C::*)(A...) const; };
// ...other qualifier specializations
The two previous traits need more specialization for different qualifiers to be more generic, e.g. const/volatile or ref-qualifiers. There are 12 combinations (or 13 including data members); a complete implementation is here.
The idea is that any qualifiers are transferred by member_class from the pointer-to-member-function type to the class itself. Then member_ptr transfers qualifiers from the class back to the pointer type. While qualifiers are on the class type, one is free to manipulate with standard traits, e.g. add or remove const, lvalue/rvalue references, etc.
Now, here is your is_foo test:
template <typename T>
struct is_foo {
private:
template<
typename Z,
typename M = decltype(&Z::foo),
typename C = typename std::decay<member_class<M>>::type,
typename S = member_type<M>
>
using pattern = member_ptr<C const, void()>;
template<typename U, U> struct helper{};
template <typename Z> static auto test(Z z) -> decltype(
helper<pattern<Z>, &Z::foo>(),
// All other requirements follow..
std::true_type()
);
template <typename> static auto test(...) -> std::false_type;
public:
enum { value = std::is_same<decltype(test<T>(std::declval<T>())),std::true_type>::value };
};
Given type Z, alias template pattern gets the correct type M of the member pointer with decltype(&Z::foo), extracts its decay'ed class C and signature S, and synthesizes a new pointer-to-member-function type with class C const and signature void(), i.e. void (C::*)() const. This is exactly what you needed: it's the same with your original hard-coded pattern, with the type Z replaced by the correct class C (possibly a base class), as found by decltype.
Graphically:
M = void (Z::*)() const -> Z + void()
-> Z const + void()
-> void (Z::*)() const == M
-> SUCCESS
M = int (Z::*)() const& -> Z const& + int()
-> Z const + void()
-> void (Z::*)() const != M
-> FAILURE
In fact, signature S wasn't needed here, so neither was member_type. But I used it in the process, so I am including it here for completeness. It may be useful in more general cases.
Of course, all this won't work for multiple overloads, because decltype doesn't work in this case.
If you simply want to check the existence of the interface on a given type T, then there're better ways to do it. Here is one example:
template<typename T>
struct has_foo
{
template<typename U>
constexpr static auto sfinae(U *obj) -> decltype(obj->foo(), bool()) { return true; }
constexpr static auto sfinae(...) -> bool { return false; }
constexpr static bool value = sfinae(static_cast<T*>(0));
};
Test code:
struct A {
void foo() {}
};
struct B : public A {
using A::foo;
};
struct C{};
int main()
{
std::cout << has_foo<A>::value << std::endl;
std::cout << has_foo<B>::value << std::endl;
std::cout << has_foo<C>::value << std::endl;
std::cout << has_foo<int>::value << std::endl;
return 0;
}
Output (demo):
1
1
0
0
Hope that helps.
Here's a simple class that passes your tests (and doesn't require a dozen of specializations :) ). It also works when foo is overloaded. The signature that you wish to check can also be a template parameter (that's a good thing, right?).
#include <type_traits>
template <typename T>
struct is_foo {
template<typename U>
static auto check(int) ->
decltype( static_cast< void (U::*)() const >(&U::foo), std::true_type() );
// ^^^^^^^^^^^^^^^^^^^
// the desired signature goes here
template<typename>
static std::false_type check(...);
static constexpr bool value = decltype(check<T>(0))::value;
};
Live example here.
EDIT :
We have two overloads of check. Both can take a integer literal as a parameter and because the second one has an ellipsis in parameter list it'll never be the best viable in overload resolution when both overloads are viable (elipsis-conversion-sequence is worse than any other conversion sequence). This lets us unambiguously initialize the value member of the trait class later.
The second overload is only selected when the first one is discarded from overload set. That happens when template argument substitution fails and is not an error (SFINAE).
It's the funky expression on the left side of comma operator inside decltype that makes it happen. It can be ill-formed when
the sub-expression &U::foo is ill-formed, which can happen when
U is not a class type, or
U::foo is inaccesible, or
there is no U::foo
the resulting member pointer cannot be static_cast to the target type
Note that looking up &U::foo doesn't fail when U::foo itself would be ambiguous. This is guaranteed in certain context listed in C++ standard under 13.4 (Address of overloaded function, [over.over]). One such context is explicit type conversion (static_cast in this case).
The expression also makes use of the fact that T B::* is convertible to T D::* where D is a class derived from B (but not the other way around). This way there's no need for deducing the class type like in iavr's answer.
value member is then initialized with value of either true_type or false_type.
There's a potential problem with this solution, though. Consider:
struct X {
void foo() const;
};
struct Y : X {
int foo(); // hides X::foo
};
Now is_foo<Y>::value will give false, because name lookup for foo will stop when it encounters Y::foo. If that's not your desired behaviour, consider passing the class in which you wish to perform lookup as a template parameter of is_foo and use it in place of &U::foo.
Hope that helps.
I suggest using decltype to generically determine the type of the member function pointers:
helper<decltype(&A::foo), &A::foo> compiles;
helper<decltype(&B::foo), &B::foo> also_compiles;
It may seem like a DRY violation, but repeating the name is fundamentally no worse than specifying the type separately from the name.
I wrote a simple class for the moving average which can be used with an AVR.
template<typename T, typename Tsum = int32_t>
class MovingAverage { ... }
But now I want to specialize this class for float without copying and pasting the whole class body and change all T and Tsum to float and that I do not need to use two template parameters. Tsum is the type for the 'sum' variable where all passed values of type T were summed up. If T is 'uint8_t' it is a good idea to use 'uint32_t' for the sum, but for float or double there is no need to use a datatype with higher precision, so I want only one parameter for this purpose. I thought it could work this way:
typedef MovingAverage<float, float> MovingAverage<float>
or this way:
template<>
class MovingAverage<float> : public MovingAverage<float, float> {};
But I was wrong and I found only solutions where I have to write my code twice.
Is there a way to write the class only once and then specialize it this way I prefer?
Thanks in advance!
If you want different default types for Tsum, this should be outsourced to another class which can be specified, for example:
template< typename, typename = void >
struct DefaultSum { using type = int32_t; };
template< typename T >
struct DefaultSum< T, typename std::enable_if<
std::is_floating_point< T >::value
>::type >
{ using type = T; };
template<typename T, typename Tsum = typename DefaultSum<T>::type >
class MovingAverage { ... }
You could write a simple traits class
// general version
template<typename T>
struct sum_type
{
typedef int32_t type;
};
// specialized version
template<>
struct sum_type<float>
{
typedef float type;
};
// repeat for double, the solution from #DanielFrey is even more sophisticated
// as it specializes all floating point types in one sweep.
and then extract this type in your class template
template<typename T, typename Tsum = typename sum_type<T>::type>
// ^^^^^^^^ <-- dependent type disambiguation
class MovingAverage { ... };
Note that this only works if your MovingAverage has a regularly parameterized implementation. If you are actually doing something special for float (e.g. rewrite expressions to take care of the non-associative character of floating point arithmetic), then you need to do more work.
If you are serious about working with C++ templates, run -not walk- to the nearest bookstore and get the book C++ Templates: The Complete Guide. Section 15.1 has a 15+ page discussion of defining a generic accumulate class template.
I want to create a simple integer range checker and converter using c++ templates.
The code looks like this:
// D is the "destination" type and S the "source" type
template <class D, class S>
inline D SafeConvert( S value );
template <class S>
inline int SafeConvert<int>( S value ) {
ASSERT( value >= S(INT_MIN) && value <= S(INT_MAX) );
return int(value);
} /// error C2768: 'SafeConvert' : illegal use of explicit template arguments
template <class S>
inline size_t SafeConvert<size_t>( S value ) {
ASSERT( value >= S(0) && value <= S(size_t(-1)) );
return size_t(value);
} /// error C2768: 'SafeConvert' : illegal use of explicit template arguments
// ...
void test() {
size_t v = INT_MAX+1;
int iv = SafeConvert<int>(v);
}
However I have the following come compilation errors:
error C2768: 'SafeConvert' : illegal use of explicit template arguments
My question is how to tell the compiler that I want to specialize only the D class ?
Thanks.
You can't partially specialize function templates. You need to mimic it with a class wrapper or use standard function overloading. An example of mimicing:
template <typename T1, typename T2>
struct processor;
template < typename T1, typename T2 >
T1 fun(T2 t2) { return processor<T1,T2>::apply(t2); }
template < typename T2 >
struct processor<int,T2>
{
static int apply(T2 t2) { .... }
};
...etc...
It's going to be a bother, and a hell to maintain.
Normally I would advise using the numeric_limits:
template <class D, class S>
D SafeConvert(S value)
{
ASSERT(value >= std::numeric_limits<D>::min()
&& value <= std::numeric_limits<D>::max());
return static_cast<D>(value);
}
However there is a warning emitted by the compiler whenever you compare a signed integer with an unsigned one... (never really understood this by the way)
So, instead of reinventing the wheel, I shall advise the use of Boost.NumericConversion and notably: boost::numeric_cast<>.
It's guaranteed to be performance free when the check is not required (ie the destination type is bigger than the source type) and otherwise perform the necessary checks.
Write a structure SafeConverter<T, S> that is used by SafeConvert. Better than partial specialization would be using std::numeric_limits, or even boost::numeric_cast, which already implements range checking in a more sophisticated way.
The latter could be implemented as follows:
template<typename T, typename S>
struct numeric_converter {
static T convert(const S& val);
}
template<typename T, typename S>
T numeric_cast(const S& val) {
typedef numeric_converter<T, S> converter;
return converter::convert(val);
}
Just write SafeConvert<size_t, S> instead of SafeConvert<size_t>, I think, to specialise only the second parameter. Noah Roberts is correct, too, on the point of partial specialisation of functions versus types.