Is it possible to write a function-like C preprocessor macro that returns 1
if its argument is defined, and 0
otherwise? Lets call it BOOST_PP_DEFINED
by analogy with the other boost preprocessor macros, which we can assume are also in play:
#define BOOST_PP_DEFINED(VAR) ???
#define XXX
BOOST_PP_DEFINED(XXX) // expands to 1
#undef XXX
BOOST_PP_DEFINED(XXX) // expands to 0
I'm expecting to use the result of BOOST_PP_DEFINED
with BOOST_PP_IIF
:
#define MAGIC(ARG) BOOST_PP_IIF(BOOST_PP_DEFINED(ARG), CHOICE1, CHOICE2)
In other words, I want the expansion of MAGIC(ARG)
to vary based on whether ARG
is defined or not at the time that MAGIC
is expanded:
#define FOO
MAGIC(FOO) // expands to CHOICE1 (or the expansion of CHOICE1)
#undef FOO
MAGIC(FOO) // expands to CHOICE2 (or the expansion of CHOICE2)
I also found it interesting (and somewhat surprising) that the following doesn't work:
#define MAGIC(ARG) BOOST_PP_IIF(defined(arg), CHOICE1, CHOICE2)
Because apparently defined
is only valid in the preprocessor when used as part of an #if
expression.
I somewhat suspect that the fact that boost preprocessor doesn't already offer BOOST_PP_DEFINED
is evidence for its impossibility, but it can't hurt to ask. Or, am I missing something really obvious about how to achieve this.
EDIT: To add some motivation, here is why I want this. The traditional way to do "API" macros to control import/export is to declare a new set of macros for every library, which means a new header, etc. So if we have class Base
in libbase
and class Derived
in libderived
, then we have something like the following:
// 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;
Now, obviously, each of the _config.hpp
header would really be a lot more complex, defining several macros. We could probably pull out some of the commonalities into a generic config_support.hpp
file, but not all. So, in an effort to simplify this mess, I wondered if it would be possible make this generic, so that one set of macros could be used, but that would expand differently based on which _COMPILING
macros were in play:
// 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;
In other words, when compiling base.cpp
, API(LIBBASE)
would expand to __declspec(dllexport)
because LIBBASE_COMPILING
was defined on the command line, but when compiling derived.cpp
API(LIBBASE)
would expand to __declspec(dllimport)
because LIBBASE_COMPILING
was not defined on the command line, but API(LIBDERIVED)
would now expand to __declspec(dllexport)
since LIBDERIVED_COMPILING
would be. But for this to work it is critical that the API
macro expand contextually.
FOO
is#define
'd ? Is it you who#define
it in various places? What type isFOO
- does it define string, integer, empty definition or something else? – DibasicFOO
is defined, and in my envisioned usage, I would be setting it on the compile line with -D when certain files are compiled. As such, I could set it to any of the choices you mention. – AmanFOO
, why not just do-DFOO=1
, then add a little#ifndef FOO / #define FOO 0 / #endif
and just useFOO
. – Batish#ifndef FOO / #define FOO 0 / #endif
gets into precompiled header. – Dibasic