gcc4.3 warning for multiple scope_exits

Boost.ScopeExit uses a tricky bit of code (from Steven Watanabe) to allow a macro expansion to contain code which will declare a variable exactly once, regardless of how many times the macro is used in a given scope. Unfortunately, gcc4.3 -Wall generates a warning for this code in the second and later uses of the macro in the same scope. The warning is comparisons like X<=Y<=Z do not have their mathematical meaning It is complaining about a statement that gets parsed as identifier < 0 > variable; Valid code, and correct in this particular case, but the warning is there never the less. I realize that boost doesn't have a no-warnings policy (dealing with compiler variations that lead to actual errors is hard enough), but since my team has a general policy of using -Werror and I've been proselytizing the use of scope_exit within our group, I was motivated to find a way to kill this warning. After studying Steven's trick (ouch! my brain hurts) I've come up with an extension of the same technique which avoids the warning, using the time honored approach of adding another level of indirection. Here's my patch: ---------- --- scope_exit.hpp.orig 2009-07-09 13:19:36.000000000 -0400 +++ scope_exit.hpp.new 2009-07-09 13:20:04.000000000 -0400 @@ -36,14 +36,21 @@ typedef void* declared; struct undeclared { declared dummy[2]; }; + template<int> + struct holder + { + declared value; + static const int apply_value = 0; + friend void operator>(int, const holder&) {} + }; + template<> struct declare<sizeof(undeclared)> { template<int> struct apply { - declared value; - friend void operator>(bool, const apply&) {} + static const int apply_value = 0; }; }; @@ -214,8 +221,9 @@ BOOST_PP_SEQ_FOR_EACH_I(BOOST_SCOPE_EXIT_AUX_MEMBER, id, seq) \ BOOST_SCOPE_EXIT_AUX_PARAMS_T_CTOR(id, seq) \ } BOOST_SCOPE_EXIT_AUX_PARAMS(id) BOOST_SCOPE_EXIT_AUX_PARAMS_INIT(id, seq)\ - boost::scope_exit::aux::declare<sizeof(boost_scope_exit_args)> \ - ::apply<0> boost_scope_exit_args; \ + boost::scope_exit::aux::holder< \ + boost::scope_exit::aux::declare<sizeof(boost_scope_exit_args)> \ + ::apply<0>::apply_value> boost_scope_exit_args; \ boost_scope_exit_args.value = &BOOST_SCOPE_EXIT_AUX_PARAMS(id); \ struct BOOST_SCOPE_EXIT_AUX_GUARD_T(id) { \ BOOST_SCOPE_EXIT_AUX_PARAMS_T(id)* boost_se_params_; \

Kim Barrett <kab.conundrums <at> verizon.net> writes:
Boost.ScopeExit uses a tricky bit of code (from Steven Watanabe) to allow a macro expansion to contain code which will declare a variable exactly once, regardless of how many times the macro is used in a given scope. Unfortunately, gcc4.3 -Wall generates a warning for this code in the second and later uses of the macro in the same scope. The warning is
comparisons like X<=Y<=Z do not have their mathematical meaning
It is complaining about a statement that gets parsed as
identifier < 0 > variable;
Valid code, and correct in this particular case, but the warning is there never the less.
Hi Kim, I don't understand how it works for the second use within one scope: boost::scope_exit::aux::holder< boost::scope_exit::aux::declare< sizeof(boost_scope_exit_args)
::apply<0>::apply_value boost_scope_exit_args;
If sizeof(boost_scope_exit_args) is equal to sizeof(declared), the resulting type would be holder<(0<0>::apply_value)>. But I don't see a global variable apply_value in your patch. BTW, I modified Steven's code and introduced a bug because sizeof(declare<sizeof(undeclared)>::apply<0>) does not necessarily equal to sizeof(declared). Alex

At 10:48 AM +0000 7/14/09, Alexander Nasonov wrote:
boost::scope_exit::aux::holder< boost::scope_exit::aux::declare< sizeof(boost_scope_exit_args)
::apply<0>::apply_value boost_scope_exit_args;
If sizeof(boost_scope_exit_args) is equal to sizeof(declared), the resulting type would be holder<(0<0>::apply_value)>. But I don't see a global variable apply_value in your patch.
The added parenthesis in the above, in holder<(0<0>::apply_value)> don't describe the actual parse and evaluation. Instead, that snippet gets parsed and evaluated as holder<(0<0)>::apply_value so that the whole (expression) statement (with disambiguating parenthesis added) is holder<(0<0)>::apply_value > boost_scope_exit_args;
BTW, I modified Steven's code and introduced a bug because sizeof(declare<sizeof(undeclared)>::apply<0>) does not necessarily equal to sizeof(declared).
Are you sure? Perhaps I'm missing something, but it looks to me like those values should indeed be the same. The "apply" class template in question has one member, of type "declared". Given that, I would be surprised if the "apply" class template could have a different size (or alignment) from the "declared" type.

Kim Barrett <kab.conundrums <at> verizon.net> writes:
At 10:48 AM +0000 7/14/09, Alexander Nasonov wrote:
boost::scope_exit::aux::holder< boost::scope_exit::aux::declare< sizeof(boost_scope_exit_args)
::apply<0>::apply_value boost_scope_exit_args;
If sizeof(boost_scope_exit_args) is equal to sizeof(declared), the resulting type would be holder<(0<0>::apply_value)>. But I don't see a global variable apply_value in your patch.
The added parenthesis in the above, in
holder<(0<0>::apply_value)>
don't describe the actual parse and evaluation. Instead, that snippet gets parsed and evaluated as
holder<(0<0)>::apply_value
This is crazy, isn't it? Q: correctly indent the following code: <see code above> A: it's impossible For the second use, the correct indentation is: boost::scope_exit::aux::holder< boost::scope_exit::aux::declare< sizeof(boost_scope_exit_args)
::apply < 0 ::apply_value > boost_scope_exit_args;
BTW, I modified Steven's code and introduced a bug because sizeof(declare<sizeof(undeclared)>::apply<0>) does not necessarily equal to sizeof(declared).
Are you sure? Perhaps I'm missing something, but it looks to me like those values should indeed be the same. The "apply" class template in question has one member, of type "declared". Given that, I would be surprised if the "apply" class template could have a different size (or alignment) from the "declared" type.
They're the same because void* is well aligned and there is no padding in struct apply<0> but strictly speaking, they don't have to be of equal size. I like explicit specializations for declared and undeclared, though. They make reading code a little bit easier (and here "a little bit" make a big difference, given how complex the trick is). Perhaps adding BOOST_STATIC_ASSERT would be enough? Alex multiplied by a complec.

At 1:38 PM +0000 7/14/09, Alexander Nasonov wrote:
This is crazy, isn't it?
No kidding!
Q: correctly indent the following code: <see code above> A: it's impossible
For the second use, the correct indentation is:
boost::scope_exit::aux::holder< boost::scope_exit::aux::declare< sizeof(boost_scope_exit_args)
::apply < 0 ::apply_value > boost_scope_exit_args;
Yes. (I *think*. See discussion of "crazy" above.)
They're the same because void* is well aligned and there is no padding in struct apply<0> but strictly speaking, they don't have to be of equal size.
Hm. Everything involved looks like POD to me (albeit with oddly spelled names, if one is a C programmer). I think lots of code would be broken if such types could be different in size. But I admit I can't right now put together a string of text in the standard that would guarantee that they are the same size.
I like explicit specializations for declared and undeclared, though. They make reading code a little bit easier (and here "a little bit" make a big difference, given how complex the trick is).
Completely agree.
Perhaps adding BOOST_STATIC_ASSERT would be enough?
I don't see any way that could hurt. Even if not necessary, it may help guide future readers in the right direction.
participants (2)
-
Alexander Nasonov
-
Kim Barrett