Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Is the valid preprocessing token verifing necessary in Macro ## concatenation(Aka: GCC: pasting x and x does not give a valid preprocessing token)

1,756 views
Skip to first unread message

Zhenghui Zhou

unread,
Nov 19, 2012, 9:24:38 AM11/19/12
to
When use ## in a macro to concat two parameters, I met a problem with some proprocessing tricks. The gcc doesn't allow it's result is not a valid preprocessing token, etc.

In most simple case, we can just remove it, but sometimes, when using the proprocessing macro as an meta progrmming language, it couldn't be ignored.

One of the example is from the preprocessor library from boost.org, a undocument interface BOOST_PP_IS_EMPTY, which can be used to check whether a processing token empty or not, the code may be:

#include <boost/preprocessor/facilities/is_empty.hpp>

BOOST_PP_IS_EMPTY() /* result: 1 */
BOOST_PP_IS_EMPTY(a) /* result: 0 */
BOOST_PP_IS_EMPTY(+a) /* error report here, for '+' cannot be pasted */

For IS_EMPTY macro there is a workaround to avoid the problem, the definitions are:
#define MY_IS_EMPTY(a) BOOST_PP_EQUAL(BOOST_PP_SEQ_SIZE(MY_IS_EMPTY_II(MY_IS_EMPTY_I(MY_PAREN_DEL(a)))), 2)
#define MY_IS_EMPTY_I(a) (BOOST_PP_CAT(MY_IS_EMPTY_I_, BOOST_PP_SEQ_SIZE(a))) )
#define MY_IS_EMPTY_I_0 MY_IS_EMPTY_I_HELPER BOOST_PP_LPAREN()
#define MY_IS_EMPTY_I_HELPER(a) )a( /* when a is empty, my may get more sequence elements of boost proprocessor */
#define MY_IS_EMPTY_II(a) BOOST_PP_CAT(MY_IS_EMPTY_II_, BOOST_PP_SEQ_SIZE(a)) )
#define MY_IS_EMPTY_II_1 (
#define MY_IS_EMPTY_II_2 ()(

The MY_PAREN_DEL macro is used for removing any parenthneses around a parameter, maybe defined as:
#define MY_PAREN_DEL(a) BOOST_PP_CAT(MY_PAREN_DEF_, MY_PAREN_DEL_1 a)
#define MY_PAREN_DEL_1(a) MY_PAREN_DEL_2 a
#define MY_PAREN_DEL_2(a) MY_PAREN_DEL_3 a
#define MY_PAREN_DEL_3(a) MY_PAREN_DEL_4 a
#define MY_PAREN_DEL_4(a) MY_PAREN_DEL_5 a
#define MY_PAREN_DEL_5(a) MY_PAREN_DEL_6 a
...

#define MY_PAREN_DEF_MY_PAREN_DEL_1
#define MY_PAREN_DEF_MY_PAREN_DEL_2
#define MY_PAREN_DEF_MY_PAREN_DEL_3
#define MY_PAREN_DEF_MY_PAREN_DEL_4
#define MY_PAREN_DEF_MY_PAREN_DEL_5
#define MY_PAREN_DEF_MY_PAREN_DEL_6
...

Then we may get:
MY_IS_EMPTY() /* result 1 */
MY_IS_EMPTY(a) /* result 0 */
MY_IS_EMPTY(+a) /* result 0 */
MY_IS_EMPTY(()) /* result 1 */
MY_IS_EMPTY((a)) /* result 0 */
...

Further more, the thing I want to achieve is an contract programming method with function invoking, which parameters are some pointers of structure types or just NULL, some auxiliary macros as below:

#define MY_REF_PRED(d, tuple) BOOST_PP_TUPLE_ELEM(2, 0, tuple)

#define MY_REF_OP(d, tuple) MY_REF_OP_D(MY_REF_OP_SEQ(BOOST_PP_TUPLE_ELEM(2, 1, tuple)))

#define MY_REF_OP_D(seq) \
BOOST_PP_IF(MY_IS_EMPTY(BOOST_PP_SEQ_HEAD(BOOST_PP_SEQ_REVERSE(seq))) \
, (1, BOOST_PP_SEQ_HEAD(seq)) \
, (0, BOOST_PP_SEQ_HEAD(BOOST_PP_SEQ_REVERSE(seq))))

#define MY_REF_OP_SEQ(ref) BOOST_PP_CAT(MY_REF_OP_DEF_, MY_REF_OP_HELPER ref) )

#define MY_REF_OP_HELPER(ref) 00(ref)

#define MY_REF_OP_DEF_00(x) (x)(

#define MY_REF_OP_DEF_MY_REF_OP_HELPER (

#define MY_REF_HELPER(ref) \
BOOST_PP_TUPLE_ELEM(2, 1, BOOST_PP_WHILE(MY_REF_PRED, MY_REF_OP, (1, ref)))

The MY_REF_HELPER macro can be used for deducing whether a parameter is NULL/0 or not:
MY_REF_HELPER(NULL) 0
MY_REF_HELPER(0) 0
MY_REF_HELPER(ptr) ptr
MY_REF_HELPER((struct base_type *)ptr) ptr
MY_REF_HELPER(&x) &x
...

For a idiom we may common use for dealing a series of kinds of data structure or object-oriented kind of programming is defined a base type in library or reusable code, and force the derived type to encapsulate it, so we can check such thing in the base code, with force the client redelare it again except during the definition.
Probable more definitions:
#define MY_REF_DEF_0 )(((struct base_type *)0))( /* sequence trick of boost preprocessor again */

#define MY_REF_SEQ(ref) (BOOST_PP_CAT(MY_REF_DEF_, MY_REF_HELPER(ref)))(ref) /* pasting error again we may meet with ## operator to deal with a & expression */

#define MY_REF(ref) BOOST_PP_SEQ_ELEM(1, MY_REF_SEQ(ref))

The MY_REF add type information to NULL/0 parameters:
MY_REF(0) => ((struct base_type *)0)
MY_REF(NULL) => ((struct base_type *)0)
MY_REF(ptr) => ptr
MY_REF((struct base_type *)ptr) => (struct base_type *)ptr
MY_REF(&x) => error here to for concating &x!!!

If works, the macro invoking can be used to encapsulate a function call with the checking of parameters' type.
#define BUILD_ASSERT_OR_ZERO(cond) (sizeof(char [1 - 2*!(cond)]) - 1)

#define fun(derived) \
__fun(derived + BUILD_ASSERT_OR_ZERO(sizeof((MY_REF(derived))->base_member) == sizeof(struct base_type))) /* or checking with typeof extension of gcc */

So the questions I want to ask are:
1. Is there any existing solution for my problem?
2. Will it be possible to allow such pasting and more meta programming convenience by the standard or by the compiler authors? Though disallowing pasting, the gcc generate same code in some of such situation without ##.

Hans-Bernhard Bröker

unread,
Nov 19, 2012, 11:53:43 AM11/19/12
to
On 19.11.2012 15:24, Zhenghui Zhou wrote:

> When use ## in a macro to concat two parameters, I met a problem with
> some proprocessing tricks. The gcc doesn't allow it's result is not
> a valid preprocessing token, etc.

[...]

You're rather clearly running into undefined behaviour there. C99
6.10.3.3p3 says that the result of applying ## must be a valid
preprocessing token. If it's not, that's undefined behaviour.

Jens Gustedt

unread,
Nov 21, 2012, 4:31:42 PM11/21/12
to
Hello,

Am 19.11.2012 15:24, schrieb Zhenghui Zhou:
> When use ## in a macro to concat two parameters, I met a problem with some proprocessing tricks. The gcc doesn't allow it's result is not a valid preprocessing token, etc.
>
> In most simple case, we can just remove it, but sometimes, when using the proprocessing macro as an meta progrmming language, it couldn't be ignored.
>
> One of the example is from the preprocessor library from boost.org, a undocument interface BOOST_PP_IS_EMPTY, which can be used to check whether a processing token empty or not, the code may be:
>
> #include <boost/preprocessor/facilities/is_empty.hpp>
>
> BOOST_PP_IS_EMPTY() /* result: 1 */
> BOOST_PP_IS_EMPTY(a) /* result: 0 */
> BOOST_PP_IS_EMPTY(+a) /* error report here, for '+' cannot be pasted */

They seem only to be dealing with the case that there is just one
alphanumeric token there, which is probably sufficient for many use
cases that Boost has for these things.

This limitation can be overcome with some more tricks that I have
written up here:

http://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/

The main idea is not to use ## to paste things together, but to see if
the macro argument when placed between a special macro and a pair of
parenthesis inhibits the expansion of that macro or not.

This still has some corner cases where it might give surprising
results, but works suitably well in daily coding; it is at the base of
P99's P99_IS_EMPTY and P99_IF_EMPTY macros that then are used to
implement default arguments for C functions/macros and stuff like
that.

Jens

Zhenghui Zhou

unread,
Nov 22, 2012, 6:03:16 AM11/22/12
to
I had read your blogs, and found many brilliant ideas there. Though I still have trouble with my deals, thanks very much.
0 new messages