Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Macro compiles with GCC but not with VS11

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?

like image 266
SalemD Avatar asked Mar 22 '23 14:03

SalemD


1 Answers

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.

like image 113
Yakk - Adam Nevraumont Avatar answered Apr 01 '23 02:04

Yakk - Adam Nevraumont