I'm using preprocessor dir开发者_开发知识库ectives to de-bloat some templated operator definitions. E.g.
#define BINARY_VECTOR_RETURN_OPERATOR(optype) \
template <typename T, typename U> \
vector<decltype(T() optype U())> operator optype (const vector<T>& A, const vector<U>& B){ \
vector<decltype(T()*U())> C; \
C.reserve(A.size()); \
typename vector<T>::const_iterator a = A.begin(); \
typename vector<U>::const_iterator b = B.begin(); \
while (a!=A.end()){ \
C.push_back((*a) optype (*b)); \
++a; ++b; \
} \
return C; \
} \
BINARY_VECTOR_RETURN_OPERATOR(*);
BINARY_VECTOR_RETURN_OPERATOR(+);
BINARY_VECTOR_RETURN_OPERATOR(-);
BINARY_VECTOR_RETURN_OPERATOR(%);
So that works fine. What I want to do now is to have two modes of operation, "debug" and "not debug" which I set via a #define DEBUG command earlier. I would like to do something like this:
#define BINARY_VECTOR_RETURN_OPERATOR(optype) \
template <typename T, typename U> \
vector<decltype(T() optype U())> operator optype (const vector<T>& A, const vector<U>& B){ \
#ifdef DEBUG
uint n = A.size(); \
if (n != B.size()){ \
char buf[BUFFLEN]; \
sprintf(buf, "Size mismatch in operator+(%s,%s), sizes: (%d, %d), crashing!", \
typeid(A).name(), typeid(B).name(), (int) A.size(), (int) B.size()); \
cout << buf << endl; \
throw("Size Mismatch Error"); \
} \
#endif
vector<decltype(T()*U())> C; \
C.reserve(A.size()); \
typename vector<T>::const_iterator a = A.begin(); \
typename vector<U>::const_iterator b = B.begin(); \
while (a!=A.end()){ \
C.push_back((*a) optype (*b)); \
++a; ++b; \
} \
return C; \
} \
but the compiler doesn't seem to like that. I could redefine the entire BINARY_VECTOR_RETURN_OPERATOR for each case using #ifdef DEBUG around the whole thing, but that's not very elegant. Is there a way to implement the code in the spirit of my second example?
You can't have a #if
inside a #define
, but you can have a #define
inside the code controlled by a #if
.
For example:
#ifdef DEBUG
#define BINARY_VECTOR_RETURN_OPERATOR(optype) \
first-part \
debug-code \
last-part
#else
#define BINARY_VECTOR_RETURN_OPERATOR(optype) \
first-part \
last-part
#endif
If first-part and last-part are big enough, you might want to define macros for them.
I'm not saying this is a good idea, but it does do what you were trying to do.
EDIT: Thanks to @okorz001 for suggesting a cleaner alternative in a comment:
#ifdef DEBUG
#define DEBUG_CODE (blah, blah)
#else
#define DEBUG_CODE /* nothing */
#endif
#define BINARY_VECTOR_RETURN_OPERATOR(optype) \
first-part \
DEBUG_CODE;
last-part
Obviously the real code would use better names.
Ugh, try not to ever use the preprocessor for actual code. It's almost always a really bad idea.
I had a go at restructuring the macro as a template function. I had some fun with the decltypes, so much so that I pulled out a traits class just to reduce the complexity of the function definition!
I don't really see a way of getting rid of the macro completely, just for sanity in the declaration of the actual operator overloads. Now, however, it's just a simple pass-through to the template function operator_impl() and in that you should be able to use #ifdefs for your debug code.
template <typename T, typename U, typename Op>
struct op_result
{
typedef vector<decltype( (*((Op*)nullptr)) (*(T*)nullptr, *(U*)nullptr) ) > type;
};
template <typename T, typename U, typename Op>
inline typename op_result<T, U, Op>::type
operator_impl(const vector<T>& A, const vector<U>& B, Op op)
{
op_result<T, U, Op>::type C;
C.reserve(A.size());
typename vector<T>::const_iterator a = A.begin();
typename vector<U>::const_iterator b = B.begin();
while (a!=A.end())
{
C.push_back(op(*a, *b));
++a; ++b;
}
return C;
}
#define BINARY_VECTOR_RETURN_OPERATOR(optype) \
template <class T, class U> \
inline vector<decltype( *(const T*)nullptr optype *(const U*)nullptr)> \
operator optype (const vector<T>& A, const vector<U>& B) \
{ \
return operator_impl(A, B, [](const T& t, const U& u) {return t optype u;}); \
}
Does an assert
or BOOST_ASSERT
do the job? (Got to admit I have never put an assert in a macro before - so behind the scenes this might just be reproducing your problem)
So, instead of
#ifdef DEBUG
uint n = A.size();
if (n != B.size()){
char buf[BUFFLEN];
sprintf(buf, "Size mismatch in operator+(%s,%s), sizes: (%d, %d), crashing!",
typeid(A).name(), typeid(B).name(), (int) A.size(), (int) B.size());
cout << buf << endl;
throw("Size Mismatch Error");
}
#endif
just write
BOOST_ASSERT(A.size() == B.size()); //include <boost/assert.hpp>
or
assert(A.size() == B.size()); //include <assert.h>
(Here are some explanitary links for boost assert and assert.h)
May be there is a way to implement the code in the spirit of your second example. You can use if() instead of #ifdef like this
// May not be useful but still...
#ifdef DEBUG
#define DEBUG 1
#else
#define DEBUG 0
#endif
#define BINARY_VECTOR_RETURN_OPERATOR(optype) \
template <typename T, typename U> \
// Code within #ifdef......... \
if(DEBUG) { \
uint n = A.size(); \
// ............. \
}// end if \
// Code after #ifdef.................. \
vector<decltype(T()*U())> C;\
return C; \
} \
Please test and see if this works.
精彩评论