开发者

C++ nested preprocessor directions

开发者 https://www.devze.com 2023-04-07 09:54 出处:网络
I\'m using preprocessor dir开发者_开发知识库ectives to de-bloat some templated operator definitions. E.g.

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号