Reputation: 118
I have written the following code to assist restricting my template functions to certain types, with meaningful error message when other types were used. I had the idea from a different question in stackoverflow, on which I still can't comment because I am new here.
The macro compiles flawlessly under linux GCC but not with Visual Studio 2012.
#include <string>
#include <iostream>
#include <vector>
#include <cassert>
#include <type_traits>
#define ISALLOWED(DerivedT) (std::is_same<T, DerivedT>::value)||(std::is_base_of<T,DerivedT>::value)
#define FE_1(WHAT, X) WHAT(X)
#define FE_2(WHAT, X, ...) WHAT(X) || FE_1(WHAT, __VA_ARGS__)
#define FE_3(WHAT, X, ...) WHAT(X) || FE_2(WHAT, __VA_ARGS__)
#define FE_4(WHAT, X, ...) WHAT(X) || FE_3(WHAT, __VA_ARGS__)
#define FE_5(WHAT, X, ...) WHAT(X) || FE_4(WHAT, __VA_ARGS__)
#define FE_6(WHAT, X, ...) WHAT(X) || FE_5(WHAT, __VA_ARGS__)
//... repeat as needed
#define GET_MACRO(_1,_2,_3,_4,_5,_6,NAME,...) NAME
#define FOR_EACH(action,...) \
GET_MACRO(__VA_ARGS__,FE_6,FE_5,FE_4,FE_3,FE_2,FE_1)(action,__VA_ARGS__)
// this is where you need to add types
#define ASSERTIOTYPES \
static_assert(FOR_EACH(ISALLOWED,\
int,double,std::string\
),"Type not defined for this template.");
template<class T> std::ostream & operator<<(std::ostream &os,
const std::vector<T> & v) {
ASSERTIOTYPES;
os << "[";
for (size_t i = 0; i < v.size(); i++) {
os << v[i];
if (i != v.size() - 1)
os << ", ";
}
os << "]";
return os;
}
The error message is: error C2977: 'std::is_same' : too many template arguments
And it appears only when I define ASSERTIOTYPES with more than one type, but when it is defined with only one type, for example:
#define ASSERTIOTYPES \
static_assert(FOR_EACH(ISALLOWED,\
int\
),"Type not defined for this template.");
... the code compiles normally.
Any idea how to solve this?
Upvotes: 4
Views: 304
Reputation: 275730
template<typename T> struct is_io_type : std::false_type {};
template<> struct is_io_type<int> : std::true_type {};
template<> struct is_io_type<double> : std::true_type {};
template<> struct is_io_type<std::string> : std::true_type {};
Then, instead of your macro, simply type:
static_assert( is_io_type<T>::value, "Type not defined for this template." )
which is about as verbose and not nearly as obscure or hard to figure out what the heck is going on.
Now, suppose we really need the is_base_of
functionality.
template<typename T,typename=void> struct is_io_type : std::false_type {};
template<> struct is_io_type<int,void> : std::true_type {};
template<typename T> struct is_io_type<T,typename std::enable_if<std::is_base_of<int, T>::type> : std::true_type {};
template<> struct is_io_type<double,void> : std::true_type {};
template<typename T> struct is_io_type<T,typename std::enable_if<std::is_base_of<double, T>::type> : std::true_type {};
template<> struct is_io_type<std::string,void> : std::true_type {};
template<typename T> struct is_io_type<T,typename std::enable_if<std::is_base_of<std::string, T>::type> : std::true_type {};
which gets pretty verbose.
But rather than creating a variardic macro expansion at the point where we use the macro, we can instead use a macro to build the above type trait specializations.
So you first create your type trait, then extend it:
CREATE_TYPE_CATEGORY( is_io_type );
ADD_BASE_TYPE_CATEGORY( is_io_type, int );
ADD_BASE_TYPE_CATEGORY( is_io_type, double );
ADD_BASE_TYPE_CATEGORY( is_io_type, std::string );
which would write the code above a bit less verbosely. It also supports distributed trait modification: if someone creates a new type that should be is_io_type
, they can use the above macro after introducing the type and it (and its descendents) become an io type.
Upvotes: 7
Reputation: 537
From the problem you described, it is most likely a bug of VA_ARGS
I suggest you either use static inlines or you can write a simple test. Copy most of your code above and check what GET_MACRO give you. Then you can post the result, that will be very helpful for further diagnositic.
Upvotes: 0