是否可以编写类似函数的C预处理器宏,如果定义了参数,则返回1
,否则0
?让我们将其称为BOOST_PP_DEFINED
,类似于其他升压预处理器宏,我们可以假设它们也在起作用:
#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_PP_DEFINED
这一事实证明它不可能,但要问它是不是很痛苦。或者,我错过了一些关于如何实现这一点的非常明显的事情。
编辑:为了增加一些动力,这就是我想要的原因。传统的做法" API"用于控制导入/导出的宏是为每个库声明一组新的宏,这意味着一个新的标题等等。因此,如果class Base
中有libbase
,class Derived
中有libderived
1}},然后我们有以下内容:
// 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
宏在进行中会有不同的扩展:< / p>
// 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
{时{1}}会扩展为API(LIBBASE)
,因为__declspec(dllimport)
在命令行中未,但LIBBASE_COMPILING
现在会扩展为API(LIBDERIVED)
,因为{ {1}}会。但要实现这一点,__declspec(dllexport)
宏在上下文中扩展至关重要。
答案 0 :(得分:4)
看起来您可以使用BOOST_VMD_IS_EMPTY
来实现所需的行为。如果输入为空,则此宏返回1
;如果输入为空,则返回0
。
根据观察发现,当XXX
定义#define XXX
时,在扩展期间将空参数列表传递给BOOST_VMD_IS_EMPTY(XXX)
。
MAGIC
宏的示例实现:
#ifndef BOOST_PP_VARIADICS
#define BOOST_PP_VARIADICS
#endif
#include <boost/vmd/is_empty.hpp>
#include <boost/preprocessor/control/iif.hpp>
#define MAGIC(XXX) BOOST_PP_IIF(BOOST_VMD_IS_EMPTY(XXX), 3, 4)
#define XXX
int x = MAGIC(XXX);
#undef XXX
int p = MAGIC(XXX);
对于Boost 1.62和VS2015预处理器输出将是:
int x = 3;
int p = 4;
这种方法存在许多缺陷,例如:如果使用XXX
定义#define XXX 1
,则无效。 BOOST_VMD_IS_EMPTY
本身有limitations。
修改强>
以下是基于API
所需的BOOST_VMD_IS_EMPTY
宏的实现:
// config.hpp
#ifndef BOOST_PP_VARIADICS
#define BOOST_PP_VARIADICS
#endif
#include <boost/vmd/is_empty.hpp>
#include <boost/preprocessor/control/iif.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_VMD_IS_EMPTY(ARG))
#define API(LIB) API_IMPL(LIB ## _COMPILING)
让我们看看预处理器将输出的内容:
// base.hpp
#include "config.hpp"
class API(LIBBASE) base {
public:
base();
};
定义LIBBASE_COMPILING
时,GCC输出:
class __attribute__((dllexport)) Base
{
public:
Base();
};
未定义LIBBASE_COMPILING
时,GCC输出:
class __attribute__((dllimport)) Base
{
public:
Base();
};
使用VS2015和GCC 5.4(Cygwin)进行测试
编辑2:
正如@acm在使用-DFOO
定义参数时提到的那样,它与-DFOO=1
或#define FOO 1
相同。在这种情况下,基于BOOST_VMD_IS_EMPTY
的方法无效。要克服它,你可以使用BOOST_VMD_IS_NUMBER
(thnx到@jv_的想法)。实现:
#ifndef BOOST_PP_VARIADICS
#define BOOST_PP_VARIADICS
#endif
#include <boost/vmd/is_number.hpp>
#include <boost/preprocessor/control/iif.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_VMD_IS_NUMBER(ARG))
#define API(LIB) API_IMPL(LIB ## _COMPILING)
答案 1 :(得分:2)
这不是纯粹的定义检查,但我们可以一直检查特定的令牌名称。
根据Paul Fultz II的Cloak注释第一原则解决方案:
首先提供基于宏扩展有条件地选择文本为0或1
的能力#define IIF(bit) PRIMITIVE_CAT(IIF_, bit)
#define IIF_0(t, f) f
#define IIF_1(t, f) t
基本连接
#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a##__VA_ARGS__
逻辑运算符(compliment and and)
#define COMPL(b) PRIMITIVE_CAT(COMPL_, b)
#define COMPL_0 1
#define COMPL_1 0
#define BITAND(x) PRIMITIVE_CAT(BITAND_, x)
#define BITAND_0(y) 0
#define BITAND_1(y) y
查看令牌是否为parens“()”
的方法#define CHECK_N(x, n, ...) n
#define CHECK(...) CHECK_N(__VA_ARGS__, 0, )
#define PROBE(x) x, 1,
#define IS_PAREN(x) CHECK(IS_PAREN_PROBE x)
#define IS_PAREN_PROBE(...) PROBE(~)
注意IS_PAREN有效,因为“IS_PAREN_PROBE X”在CHECK()中转入一个arg,其中“IS_PAREN_PROBE()”变为PROBE(〜)变为〜,1。此时我们可以拿起1来自CHECK
根据需要吃一些宏参数的另一个实用程序
#define EAT(...)
在这里,我们利用蓝色绘画(防止天真递归宏的东西)来检查两个令牌是否相同。如果它们崩溃到()。否则,我们可以通过IS_PAREN检测到。
这取决于任何给定符号
存在的COMPARE_XXX身份宏#define PRIMITIVE_COMPARE(x, y) IS_PAREN(COMPARE_##x(COMPARE_##y)(()))
我们为该助手添加了一个IS_COMPARABLE特征
#define IS_COMPARABLE(x) IS_PAREN(CAT(COMPARE_, x)(()))
我们通过检查两个args是否具有可比性,然后转换为primitive_compare(如果它们),向后工作到EQUAL。如果没有,我们就不平等,吃掉以下的算法。
#define NOT_EQUAL(x, y) \
IIF(BITAND(IS_COMPARABLE(x))(IS_COMPARABLE(y))) \
(PRIMITIVE_COMPARE, 1 EAT)(x, y)
EQUAL是赞美
#define EQUAL(x, y) COMPL(NOT_EQUAL(x, y))
最后,我们真正想要的宏。
首先我们启用“BUILDING_LIB”的比较
#define COMPARE_BUILDING_LIB(x) x
然后我们的实际决定宏,如果符号解析为“BUILDING_LIB”,则为整数
#define YES_IF_BUILDING_LIB(name) IIF(EQUAL(name, BUILDING_LIB))("yes", "no")
#include <iostream>
#define FOO BUILDING_LIB
int main(int, char**) {
std::cout << YES_IF_BUILDING_LIB(FOO) << "\n";
std::cout << YES_IF_BUILDING_LIB(BAR) << "\n";
}
哪个输出:
yes
no
请参阅他的精彩博文(我发誓):C Preprocessor tricks, tips, and idioms
答案 2 :(得分:-1)
由于您打算使用FOO
作为您控制的文件级别切换,我建议您使用更简单的解决方案。建议的解决方案更容易阅读,不那么令人惊讶,不需要肮脏的魔法。
而不是#define MAGIC(ARG) BOOST_PP_IIF(BOOST_PP_DEFINED(ARG), CHOICE1, CHOICE2)
您只需-D
每个文件MAGIC=CHOICE1
或MAGIC=CHOICE2
。
MAGIC
但未做出选择。CHOICE1
或CHOICE2
是您不希望指定的主要默认设置,则可以使用-D
为所有文件设置默认值-U
+ -D
更改每个文件的决定。CHOICE1
或CHOICE2
很长,您可以在最初打算定义#define CHOICE1_TAG actual_contents
的头文件中MAGIC
,然后-D
MAGIC=CHOICE1_TAG
CHOICE1_TAG
1}},因为actual_contents
会自动扩展为<div class="mydiv">
<p>Item to be Centered!</p>
</div>
。