acm*_*acm 6 c++ macros boost c-preprocessor boost-preprocessor
是否有可能编写一个类似函数的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
Run Code Online (Sandbox Code Playgroud)
我期待使用的结果BOOST_PP_DEFINED有BOOST_PP_IIF:
#define MAGIC(ARG) BOOST_PP_IIF(BOOST_PP_DEFINED(ARG), CHOICE1, CHOICE2)
Run Code Online (Sandbox Code Playgroud)
换句话说,我希望扩展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)
Run Code Online (Sandbox Code Playgroud)
我还发现以下内容不起作用(有点令人惊讶):
#define MAGIC(ARG) BOOST_PP_IIF(defined(arg), CHOICE1, CHOICE2)
Run Code Online (Sandbox Code Playgroud)
因为defined当用作#if表达式的一部分时,显然只在预处理器中有效.
我有点怀疑增强预处理器尚未提供BOOST_PP_DEFINED的事实是其不可能性的证据,但它不会有问题.或者,我错过了一些关于如何实现这一点的非常明显的事情.
编辑:为了增加一些动力,这就是为什么我想要这个.执行"API"宏来控制导入/导出的传统方法是为每个库声明一组新的宏,这意味着一个新的标头,等等.因此,如果我们有class Basein libbase和class Derivedin 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;
Run Code Online (Sandbox Code Playgroud)
现在,显然,每个_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;
Run Code Online (Sandbox Code Playgroud)
换句话说,编译时base.cpp,API(LIBBASE)将扩大到__declspec(dllexport)因为LIBBASE_COMPILING在命令行上定义,但在编译时derived.cpp API(LIBBASE)将扩大到__declspec(dllimport)因为LIBBASE_COMPILING是不是在命令行上定义的,但API(LIBDERIVED)现在将扩大到__declspec(dllexport)自LIBDERIVED_COMPILING会.但是为了实现这一点,API宏必须在上下文中扩展.
看起来您可以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);
Run Code Online (Sandbox Code Playgroud)
对于 Boost 1.62 和 VS2015 预处理器输出将是:
int x = 3;
int p = 4;
Run Code Online (Sandbox Code Playgroud)
这种方法有许多缺陷,例如,如果XXX使用#define XXX 1. BOOST_VMD_IS_EMPTY本身有局限性。
编辑:
以下是所需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)
Run Code Online (Sandbox Code Playgroud)
让我们看看预处理器将输出什么:
// base.hpp
#include "config.hpp"
class API(LIBBASE) base {
public:
base();
};
Run Code Online (Sandbox Code Playgroud)
当LIBBASE_COMPILING所定义,GCC输出:
class __attribute__((dllexport)) Base
{
public:
Base();
};
Run Code Online (Sandbox Code Playgroud)
当LIBBASE_COMPILING未定义时,GCC 输出:
class __attribute__((dllimport)) Base
{
public:
Base();
};
Run Code Online (Sandbox Code Playgroud)
使用 VS2015 和 GCC 5.4 (Cygwin) 测试
编辑 2:
正如@acm 所提到的,当用-DFOO它定义的参数与-DFOO=1or 相同时#define FOO 1。在这种情况下,基于的方法BOOST_VMD_IS_EMPTY不起作用。为了克服它,您可以使用BOOST_VMD_IS_NUMBER(thnx to @jv_ for the idea)。执行:
#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)
Run Code Online (Sandbox Code Playgroud)