【问题标题】:C++ nested preprocessor directionsC++ 嵌套预处理器方向
【发布时间】:2011-09-26 15:11:02
【问题描述】:

我正在使用预处理器指令来消除一些模板化的运算符定义。例如

#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(%);

所以效果很好。我现在想要做的是有两种操作模式,“调试”和“非调试”,这是我之前通过#define DEBUG 命令设置的。我想做这样的事情:

#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; \
} \

但编译器似乎不喜欢这样。我可以使用 #ifdef DEBUG 围绕整个事情重新定义每个案例的整个 BINARY_VECTOR_RETURN_OPERATOR,但这不是很优雅。有没有办法按照我的第二个例子的精神来实现代码?

【问题讨论】:

  • AFAIK 没有办法做到这一点。我相信你知道这一点,但我会再说一遍。不要这样做,单独定义每个运算符。它不是一个很大的功能,它会大大增加文件的大小;您甚至可以通过在单行上声明这些迭代器来至少删减一行。并且调试模式的错误检查代码可以重构为一个独立的函数。另外,我认为NDEBUG(用于“非调试”)比DEBUG 符号更常见,因此将#ifdef DEBUG 更改为#ifndef NDEBUG 可能会更好。
  • 感谢您的建议。鉴于它实际上只有 4 个运算符,而且它们相当短,我同意使用 #define 来节省空间是一个坏主意。
  • 附带说明,我建议您使用ostringstream 而不是sprintf。毕竟这是 C++。
  • 好建议!在您的推动下,我想出了一个更好的方法:ostringstream oss (ostringstream::out); oss

标签: c++ compiler-construction


【解决方案1】:

#define 内不能有#if,但#if 控制的代码内可以有#define

例如:

#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

如果 first-part 和 last-part 足够大,您可能需要为它们定义宏。

我并不是说这是一个好主意,但它确实做到了你想做的事情。

编辑:感谢 @okorz001 在评论中提出更清洁的替代方案:

#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

显然真正的代码会使用更好的名称。

【讨论】:

  • 虽然,您也可以有条件地定义调试代码部分并始终在宏中“调用”它。 (类似于断言的工作方式。)
  • @Keith Thompson,实际上我最初尝试了您在第二个代码块中的建议。我想我遇到了麻烦,因为#define 命令在每一行都需要\(不跳过行),而且我可能没有正确的语法。一个简单的例子真的可以为你编译吗?
【解决方案2】:

呃,尽量不要将预处理器用于实际代码。这几乎总是一个非常糟糕的主意。

我尝试将宏重组为模板函数。我对 decltypes 有一些乐趣,以至于我提取了一个特征类,只是为了降低函数定义的复杂性!

我并没有真正看到完全摆脱宏的方法,只是为了在实际运算符重载的声明中保持理智。但是,现在它只是对模板函数 operator_impl() 的简单传递,因此您应该能够在调试代码中使用 #ifdefs。

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;}); \
}

【讨论】:

  • 感谢您查看此内容。看到它变得多么讨厌,我同意不使用预处理器进行编码是个好主意!
【解决方案3】:

assertBOOST_ASSERT 能胜任这项工作吗? (不得不承认我以前从未在宏中添加过断言 - 所以在幕后这可能只是在重现您的问题)

所以,而不是

 #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

随便写

BOOST_ASSERT(A.size() == B.size());  //include  <boost/assert.hpp>

assert(A.size() == B.size());   //include <assert.h>

(这里是boost assertassert.h 的一些解释性链接)

【讨论】:

    【解决方案4】:

    可能有一种方法可以按照您的第二个示例的精神来实现代码。 您可以像这样使用 if() 而不是 #ifdef

    // 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; \
    } \
    

    请测试一下,看看这是否有效。

    【讨论】:

    • 这不一样。现在条件是在运行时完成的,而不是编译。
    猜你喜欢
    • 2015-03-29
    • 2014-12-04
    • 1970-01-01
    • 2017-06-28
    • 1970-01-01
    • 1970-01-01
    • 2011-10-04
    • 1970-01-01
    相关资源
    最近更新 更多