【发布时间】:2017-04-18 01:48:39
【问题描述】:
是否可以编写一个类似函数的 C 预处理器宏,如果其参数已定义,则返回 1,否则返回 0?让我们称之为 BOOST_PP_DEFINED 与其他 boost 预处理器宏类比,我们可以假设它们也在起作用:
#define BOOST_PP_DEFINED(VAR) ???
#define XXX
BOOST_PP_DEFINED(XXX) // expands to 1
#undef XXX
BOOST_PP_DEFINED(XXX) // expands to 0
我希望将BOOST_PP_DEFINED 的结果与BOOST_PP_IIF 一起使用:
#define MAGIC(ARG) BOOST_PP_IIF(BOOST_PP_DEFINED(ARG), CHOICE1, CHOICE2)
换句话说,我希望MAGIC(ARG) 的扩展根据ARG 在MAGIC 扩展时是否定义而有所不同:
#define FOO
MAGIC(FOO) // expands to CHOICE1 (or the expansion of CHOICE1)
#undef FOO
MAGIC(FOO) // expands to CHOICE2 (or the expansion of CHOICE2)
我还发现以下 不起作用 很有趣(并且有些令人惊讶):
#define MAGIC(ARG) BOOST_PP_IIF(defined(arg), CHOICE1, CHOICE2)
因为显然defined 仅在用作#if 表达式的一部分时才在预处理器中有效。
我有点怀疑 boost 预处理器还没有提供 BOOST_PP_DEFINED 的事实证明了它是不可能的,但问一下也无妨。或者,我是否遗漏了一些关于如何实现这一目标的非常明显的东西。
编辑:为了增加动力,这就是我想要这个的原因。执行“API”宏来控制导入/导出的传统方法是为每个库声明一组新的宏,这意味着一个新的标头等。所以如果我们在libbase 中有class Base 和在class Derived libderived,那么我们有如下内容:
// base_config.hpp
#if LIBBASE_COMPILING
#define LIBBASE_API __declspec(dllexport)
#else
#define LIBBASE_API __declspec(dllimport)
// base.hpp
#include "base_config.hpp"
class LIBBASE_API base {
public:
base();
};
// base.cpp
#include "base.hpp"
base::base() = default;
// derived_config.hpp
#if LIBDERIVED_COMPILING
#define LIBDERIVED_API __declspec(dllexport)
#else
#define LIBDERIVED_API __declspec(dllimport)
// derived.hpp
#include "derived_config.hpp"
#include "base.hpp"
class LIBDERIVED_API derived : public base {
public:
derived();
};
// derived.cpp
#include "derived.hpp"
derived::derived() = default;
现在,很明显,每个_config.hpp 标头确实要复杂得多,定义了几个宏。我们可能会将一些共性提取到一个通用的config_support.hpp 文件中,但不是全部。因此,为了简化这种混乱,我想知道是否可以将其设为通用,以便可以使用一组宏,但这会根据所使用的_COMPILING 宏进行不同的扩展:
// config.hpp
#define EXPORT __declspec(dllexport)
#define IMPORT __declspec(dllimport)
#define API_IMPL2(COND) BOOST_PP_IIF(COND, EXPORT, IMPORT)()
#define API_IMPL(ARG) API_IMPL2(BOOST_PP_DEFINED(ARG))
#define API(LIB) API_IMPL(LIB ## _COMPILING)
// base.hpp
#include "config.hpp"
class API(LIBBASE) base {
public:
base();
};
// base.cpp
#include "base.hpp"
base::base() = default;
// derived.hpp
#include "config.hpp"
#include "base.hpp"
class API(LIBDERIVED) derived : public base {
public:
derived();
};
// derived.cpp
#include "derived.hpp"
derived::derived() = default;
换句话说,在编译base.cpp时,API(LIBBASE)会扩展为__declspec(dllexport),因为LIBBASE_COMPILING是在命令行中定义的,但是在编译derived.cpp时API(LIBBASE)会扩展为__declspec(dllimport),因为@ 987654352@ 没有在命令行上定义,但 API(LIBDERIVED) 现在将扩展为 __declspec(dllexport),因为 LIBDERIVED_COMPILING 将是。但要使其发挥作用,API 宏必须根据上下文展开。
【问题讨论】:
-
在一般情况下可能是不可能的,但在有限的情况下是可能的。您是否控制
FOO是#define'd 的方式?是你在各个地方#define它吗?FOO是什么类型 - 它定义了字符串、整数、空定义还是其他? -
@Codeguard 我确实控制了
FOO的定义方式,并且在我设想的用法中,当某些文件被编译时,我将使用 -D 在编译行上设置它。因此,我可以将其设置为您提到的任何选项。 -
如果你可以选择
FOO的值,为什么不直接做-DFOO=1,然后加一点#ifndef FOO / #define FOO 0 / #endif,就用FOO。 -
如果
#ifndef FOO / #define FOO 0 / #endif进入预编译头文件,这将中断。 -
@rodrigo 我添加了一个示例来帮助激发我为什么需要上下文扩展,以及为什么只定义和使用 #if 不起作用。
标签: c++ boost macros c-preprocessor boost-preprocessor