Can BOOST_PP_DEFINED be implemented?

Is it possible to write a macroprocessor macro C that returns 1 if its argument is specified, and 0 otherwise? Let's call it BOOST_PP_DEFINED by analogy with other boost preprocessor macros, which we can assume are also in the game:

 #define BOOST_PP_DEFINED(VAR) ??? #define XXX BOOST_PP_DEFINED(XXX) // expands to 1 #undef XXX BOOST_PP_DEFINED(XXX) // expands to 0 

I expect 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 MAGIC(ARG) extension to change depending on whether the ARG defined or not during the MAGIC extension:

 #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 was also interested (and somewhat surprisingly) that the following did not work:

 #define MAGIC(ARG) BOOST_PP_IIF(defined(arg), CHOICE1, CHOICE2) 

Because, obviously, defined only works in the preprocessor when used as part of the #if expression.

I somewhat suspect that the fact that the preprocessor accelerator no longer offers BOOST_PP_DEFINED indicates its impossibility, but this can not stop asking. Or I will miss something really obvious about how to achieve this.

EDIT . To add motivation, that’s why I want it. The traditional "API" macro method for managing import / export is to declare a new set of macros for each library, which means a new header, etc. Therefore, 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 headers will be much more complex by defining several macros. We could probably bring some similarities to a common config_support.hpp file, but not all. Therefore, in order to simplify this mess, I wondered if it was possible to do this in general so that one set of macros could be used, but this would expand in different ways based on what _COMPILING macros were in the game:

 // 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 , the API(LIBBASE) will expand to __declspec(dllexport) because LIBBASE_COMPILING was defined on the command line, but when compiling the derived.cpp API(LIBBASE) will expand to __declspec(dllimport) , because LIBBASE_COMPILING not defined on the command line, but the API(LIBDERIVED) will now expand to __declspec(dllexport) , since LIBDERIVED_COMPILING will be. But for this it is important that the API macro expands contextually.

+6
source share
3 answers

It looks like you could use BOOST_VMD_IS_EMPTY to implement the required behavior. This macro returns 1 if its input is empty or 0 if its input is not empty.

The trick based on the observation that when XXX is defined by #define XXX , an empty parameter list is passed to BOOST_VMD_IS_EMPTY(XXX) during the extension.

MAGIC macro implementation example:

 #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); 

For Boost 1.62 and VS2015 preprocessor output will be:

 int x = 3; int p = 4; 

This approach has several disadvantages, for example. it does not work if XXX is defined using #define XXX 1 . BOOST_VMD_IS_EMPTY itself has limitations .

EDIT:

Here is the implementation of the necessary API macros based on 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) 

See what the preprocessor will output for:

 // base.hpp #include "config.hpp" class API(LIBBASE) base { public: base(); }; 

When LIBBASE_COMPILING defined, GCC output:

 class __attribute__((dllexport)) Base { public: Base(); }; 

If LIBBASE_COMPILING not defined, GCC output:

 class __attribute__((dllimport)) Base { public: Base(); }; 

Tested with VS2015 and GCC 5.4 (Cygwin)

EDIT 2: Because @acm is mentioned when a parameter defined with -DFOO has a value of -DFOO=1 or #define FOO 1 . In this case, an approach based on BOOST_VMD_IS_EMPTY does not work. To overcome this, you can use BOOST_VMD_IS_NUMBER (thnx to @jv_ for this idea). Implementation:

 #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) 
+4
source

This is not a pure specific check, but we can fully verify the specific token name.

Annotation of the first decision of principles based on Cloak by Paul Fultz II:

First, indicate the ability to conditionally select text based on macro expansion to 0 or 1

 #define IIF(bit) PRIMITIVE_CAT(IIF_, bit) #define IIF_0(t, f) f #define IIF_1(t, f) t 

Basic concatenation

 #define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__) #define PRIMITIVE_CAT(a, ...) a##__VA_ARGS__ 

Logical operators (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 

A way to check if the token is or is not a parser "()"

 #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(~) 

Note. IS_PAREN works because "IS_PAREN_PROBE X" is included in one argument in CHECK (), where "IS_PAREN_PROBE ()" turns into PROBE (~), which turns into ~, 1. At this point, we can pick 1 from CHECK

Another utility to eat the necessary macros

 #define EAT(...) 

Here we use the blue picture (a thing that prevents naively recursive macros) to check if the two tokens are the same. If they fall on (). Otherwise, no, which we can detect through IS_PAREN.

It depends on existing COMPARE_XXX authentication macros for any given character.

 #define PRIMITIVE_COMPARE(x, y) IS_PAREN(COMPARE_##x(COMPARE_##y)(())) 

We are adding the IS_COMPARABLE flag for this helper.

 #define IS_COMPARABLE(x) IS_PAREN(CAT(COMPARE_, x)(())) 

We work back to EQUAL, checking to see if both arguments are matched and then converted to primitive_compare, if any. If not, we are not equal and eat the following arguments.

 #define NOT_EQUAL(x, y) \ IIF(BITAND(IS_COMPARABLE(x))(IS_COMPARABLE(y))) \ (PRIMITIVE_COMPARE, 1 EAT)(x, y) 

EQUAL is a compliment

 #define EQUAL(x, y) COMPL(NOT_EQUAL(x, y)) 

And finally, the macro that we really want.

First we will enable the comparison for "BUILDING_LIB"

 #define COMPARE_BUILDING_LIB(x) x 

Then our actual deciding macro, which is an integer, if on whether the symbol "BUILDING_LIB" is allowed

 #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"; } 

What outputs:

 yes no 

Check out his great blog post (which I cut): C Tricks, Tips, and Preprocessor Idioms

+2
source

Since you intend to use FOO as a level switch for the file you control, I suggest you use a simpler solution. The proposed solution is easier to read, less surprising, does not require dirty magic.

Instead of #define MAGIC(ARG) BOOST_PP_IIF(BOOST_PP_DEFINED(ARG), CHOICE1, CHOICE2) you simply -D with MAGIC=CHOICE1 or MAGIC=CHOICE2 for each file.

  • You do not need to do this for all files. The compiler will tell you when you used MAGIC in the file, but did not make a choice.
  • If CHOICE1 or CHOICE2 is the primary default, which you do not want to specify, you can use -D to set the default for all files and -U + -D to change your decision to a file.
  • If CHOICE1 or CHOICE2 is long, you can #define CHOICE1_TAG actual_contents in your header file, where you originally intended to define MAGIC , and then -D with MAGIC=CHOICE1_TAG , because CHOICE1_TAG will be automatically expanded to actual_contents .
-one
source

Source: https://habr.com/ru/post/1012852/


All Articles