Boost 1.49 BOOST_PP_ITERATION_FLAGS(): causes build failures; would like a boost maintainer comment

Would really appreciate if ticket 6631 is a Boost bug that I can patch for Debian, or whether it is just API breakage that boost-using packages (like luabind) must adapt to. Thanks, -Steve #6631: BOOST_PP_ITERATION_FLAGS(): error: missing binary operator before token "(" --------------------------------+------------------------------------------- Reporter: peter@??? | Owner: no-maintainer Type: Bugs | Status: new Milestone: To Be Determined | Component: preprocessor Version: Boost 1.49.0 | Severity: Regression Resolution: | Keywords: BOOST_PP_ITERATION_FLAGS --------------------------------+------------------------------------------- Changes (by Steve Robbins <smr@???>): * cc: smr@??? (added) Comment: I am currently investigating building all Debian packages with Boost 1.49 and "luabind" fails to build with very similar symptoms. Can the boost maintainer comment whether this is a bug in boost or a simple API break. Thanks, -Steve

On Sat, 10 Mar 2012 22:04:56 -0600, Steve M. Robbins wrote:
Would really appreciate if ticket 6631 is a Boost bug that I can patch for Debian, or whether it is just API breakage that boost-using packages (like luabind) must adapt to.
Thanks, -Steve
The problem is that GCC is doing syntax analysis on #if/#elif conditional expressions on non-taken branches (which is highly questionable behavior at best). E.g. // 1.cpp #if !defined(MACRO) // ... #elif MACRO() // ... #endif $ g++ --version g++ (GCC) 4.6.3 Copyright (C) 2011 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ g++ -E -P -std=c++0x -D BOOST_PP_VARIADICS -I $BOOST_ROOT -I . 1.cpp 1.cpp:4:12: error: missing binary operator before token "(" A necessary change occurred between Boost 1.48 and 1.49 in the internal definition of BOOST_PP_ITERATION_FLAGS. Essentially, BOOST_PP_ITERATION_FLAGS() used to expand to (e.g. on the first iteration depth) BOOST_PP_ITERATION_FLAGS_1 which, in turn, expanded to whatever numeric value was specified. However, in 1.49, BOOST_PP_ITERATION_FLAGS() now expands to BOOST_PP_ITERATION_FLAGS_1() which, in turn, expands to whatever numeric value was specified. However, the change to a function- like macro makes GCC choke for the above reason. The choke does not occur during the iterations, but rather after it. After the iteration, the iteration depth is 0 (i.e. not an iteration). That makes BOOST_PP_ITERATION_FLAGS() expand to (BOOST_PP_ITERATION_FLAGS_0()) which GCC chokes on after it has already taken (and executed) the body of the preceding #if branch. For example, // 1.cpp #if !BOOST_PP_IS_ITERATING #include <boost/preprocessor/iteration/iterate.hpp> #define BOOST_PP_ITERATION_PARAMS_1 (4, (1, 5, "1.cpp", 123)) #include BOOST_PP_ITERATE() post: BOOST_PP_ITERATION_FLAGS() #elif BOOST_PP_ITERATION_DEPTH() == 1 \ && BOOST_PP_ITERATION_FLAGS() == 123 \ /**/ BOOST_PP_ITERATION(): BOOST_PP_ITERATION_FLAGS() #endif $ g++ -E -P -std=c++0x -D BOOST_PP_VARIADICS -I $BOOST_ROOT -I . 1.cpp 1: (123) 2: (123) 3: (123) 4: (123) 5: (123) 1.cpp:12:1: error: missing binary operator before token "(" post: (BOOST_PP_ITERATION_FLAGS_0()) The standard "fix" is to alter the code to something like: #if !defined(MACRO) // ... #else #if MACRO() // ... #endif #endif I.e. the #elif branch needs to change to an #else branch with a nested #if/ #endif. GCC really needs to stop doing this opportunistic parsing even though it does not appear to be strictly disallowed by the standards. It didn't used to do it, but it broke quite a bit of stuff like this when it started. Regards, Paul Mensonides

On Sun, 11 Mar 2012 16:33:49 +0000, Paul Mensonides wrote:
#if !defined(MACRO) // ... #else #if MACRO() // ... #endif #endif
In the original scenario: #if !defined(MACRO) // ... #elif MACRO() // ... #endif The undefined name MACRO is replaced by 0, so the parser actually sees 0() and hence the error. I suppose that I *could* define all of these (it isn't just BOOST_PP_ITERATION_FLAGS) to have values in the non-iterating case such as: #define BOOST_PP_ITERATION_FLAGS_0() 0 // etc. However, that comes at the cost of giving a defined value to something that should not have a defined value (i.e. should be an error when used outside an iteration). It also doesn't solve the general problem introduced by GCC's overzealous parsing which produces behavior that is positively bizarre: #if !defined(MACRO) #ifndef NDEBUG #define MACRO() 123 #endif #elif MACRO() // ... #endif $ g++ -E -P -std=c++0x 1.cpp $ g++ -E -P -std=c++0x -D NDEBUG 1.cpp 1.cpp:5:12: error: missing binary operator before token "(" Regards, Paul Mensonides

Paul, Thanks a bunch for the detailed explanation. I think I know how to fix the one package that I know has been broken by this. On Sun, Mar 11, 2012 at 04:49:26PM +0000, Paul Mensonides wrote:
I suppose that I *could* define all of these (it isn't just BOOST_PP_ITERATION_FLAGS) to have values in the non-iterating case such as:
#define BOOST_PP_ITERATION_FLAGS_0() 0 // etc.
If I were to pursue this option, which other macros would need to be defined? Thanks, -Steve

On Tue, 13 Mar 2012 08:26:06 -0500, Steve M. Robbins wrote:
I suppose that I *could* define all of these (it isn't just BOOST_PP_ITERATION_FLAGS) to have values in the non-iterating case such as:
#define BOOST_PP_ITERATION_FLAGS_0() 0 // etc.
If I were to pursue this option, which other macros would need to be defined?
This isn't a good option. Essentially, GCC makes the code pattern... #if !defined(MACRO) // A #elif MACRO() // B #endif ...untenable (or at least dependent on the contents of A). This is not a problem with the definitions in the pp-lib. This is a general issue with either GCC or the C/C++ languages themselves (if GCC is actually correctly implementing the *intent* of the standards). The fix and/or workaround is to change the usage pattern to the (more verbose): #if !defined(MACRO) // A #else #if MACRO() // B #endif #endif Regards, Paul Mensonides
participants (2)
-
Paul Mensonides
-
Steve M. Robbins