【问题标题】:Avoid expansion of macros while using boost preprocessor sequences在使用 boost 预处理器序列时避免宏扩展
【发布时间】:2018-05-06 05:40:18
【问题描述】:

我正在尝试将操作系统和编译器名称作为 C++ 中的字符串。尽管对此有很多问题,但我没有找到明确的答案。所以我尝试使用 Boost.Predef 1.55,它定义了 BOOST_OS_<OS> 和 BOOST_OS_<OS>_NAME 类型的宏。

因此,对于每个操作系统增强支持,您都可以简单地使用if(BOOST_OS_<OS>) return BOOST_OS_<OS>_NAME;。对于使用COMP 而不是OS 的编译器也是如此。为了避免重复,我想使用 Boost.Preprocessor 并将它们全部放在一个循环中。

我想出的是这样的:

#define MAKE_STMT_I2(PREFIX) if(PREFIX) return PREFIX ## _NAME;
#define MAKE_STMT_I(type, curName) MAKE_STMT_I2(BOOST_ ## type ## _ ## curName)
#define MAKE_STMT(s, type, curName) MAKE_STMT_I(type, curName)
#define OS_LIST (AIX)(AMIGAOS)(ANDROID)(BEOS)(BSD)(CYGWIN)(HPUX)(IRIX)(LINUX)(MACOS)(OS400)(QNX)(SOLARIS)(UNIX)(SVR4)(VMS)(WINDOWS)(BSDI)(DRAGONFLY)(BSD_FREE)(BSD_NET)(BSD_OPEN)

    BOOST_PP_SEQ_FOR_EACH(MAKE_STMT, OS, OS_LIST)

但是我遇到了值很快扩展到的问题。例如。 VMS 已经定义了一个名为 VMS 的宏,然后将其替换为 OS_LIST。即使做类似#define OS_LIST (##AIX##)(##AMIGAOS##)(... 的事情也无济于事,因为它似乎在以后会得到扩展。

如何才能完全避免序列中的扩展?

【问题讨论】:

  • 在令牌中包含_s?
  • 什么意思?如果我这样做 VMS_s 我将无法从那里获得 boost 宏
  • 将_ 从## _ ## 中的MAKE_STMT_I 移动到_CYGWIN 或者你有什么。
  • 大部分操作系统宏都是这样定义的。所以这会使情况变得更糟。 boost.org/doc/libs/1_55_0/libs/predef/doc/html/predef/reference/…
  • (##AIX##) 也是非法的,因为(AIX) 不是单个预处理器令牌。

标签: c++ boost preprocessor boost-preprocessor


【解决方案1】:

由于您依赖未定义的令牌VMS,因此快速解决方案是简单的#undef VMS。显然,为避免破坏依赖于该宏的代码,您应该将 Boost PP 代码放在其自己的 .cpp 文件中。

【讨论】:

  • 是的,我也在考虑这个问题。不过会有很多 undef。
  • 每当您使用预处理器变得聪明时,您就是在自找麻烦。有没有办法使用其他地方没有使用的宏名称?
  • 我需要去例如BOOST_OS_VMS 和 BOOST_OS_VMS_NAME 并希望避免重复。所以可能我可以使用的唯一变体是OS_VMS(所有前缀都是OS_,之前不应该定义),代价是有点重复。
【解决方案2】:
如何才能完全避免序列中的扩展?

你不能。将高级数据结构作为参数传递给宏必然涉及评估数据结构。

您可以避免这个问题,并且仍然可以通过以下三种方式使用 boost 宏:

1。调用前取消定义问题宏

这基本上是 MSalters 推荐的。

这个想法是,如果 VMS 没有定义,它的评估不会扩展它。

在这里,您有VMS 未定义的风险,这可能会产生可怕的后果,因此您必须减轻这种情况(MSalters 谈到了这一点)。

2。从不同的数据构建高级宏

2 例如可能使用:

#define OS_LIST (S_AIX)(S_BEOS)(S_VMS)

...并要求您更改 MAKE_STMT 宏复合体;例如,这个:

#define MAKE_STMT_I2(PREFIX) if(PREFIX) return PREFIX ## _NAME;
#define MAKE_STMT_I(curName) MAKE_STMT_I2(BOOST_O ## curName)
#define MAKE_STMT(s, type, curName) MAKE_STMT_I(curName)
#define OS_LIST (S_AIX)(S_AMIGAOS)(S_ANDROID)(S_BEOS)(S_BSD)(S_CYGWIN)(S_HPUX)(S_IRIX)(S_LINUX)(S_MACOS)(S_OS400)(S_QNX)(S_SOLARIS)(S_UNIX)(S_SVR4)(S_VMS)(S_WINDOWS)(S_BSDI)(S_DRAGONFLY)(S_BSD_FREE)(S_BSD_NET)(S_BSD_OPEN)

(注意:这里我忽略了类型;无论如何都没有必要将OS 作为数据传递给迭代序列)。

这里的想法是找到BOOST_OS_FOO 和BOOST_OS_FOO_NAME 的不同共享部分来放入您的数据,这样您的数据就不会包含您定义的宏。

在这里,您冒着S_FOO 被定义在更高级别的风险,这会让您感到困惑。您可以通过在数据中找到不同的部分来缓解这种情况。

3。构建包装标识符

这是最容易通过示例定义的:

#define OS_LIST (AIX)(BEOS)(8VMS)
#define BOOST_OS_8VMS BOOST_OS_VMS
#define BOOST_OS_8VMS_NAME BOOST_OS_VMS_NAME

这里的想法是您正在构建不同 BOOST_OS_xxx / BOOST_OS_xxx_NAME 表单宏,然后将它们重新映射回所需的宏。使用数字前缀具有不受扩展影响的优势(此类实体是有效的预处理器标记 (pp-numbers),但它们不能是类似对象的宏)。

【讨论】:

  • 2 本质上是我对 MSalters 答案的评论中的想法。 3 是无法忍受的,因为这根本不会为我节省任何工作,但很高兴知道数字前缀技巧。
猜你喜欢
  • 1970-01-01
  • 2013-03-18
  • 2018-01-04
  • 1970-01-01
  • 2023-03-07
  • 2020-10-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多