0

Background

I have some macros of the form ATTR(XXX(...)). ATTR processes XXX into a macro ATTR_IMPL_XXX(...), and XXX can have a variable number of arguments (these variadic args are just passed to another macro, and can be empty).

In the case there are no args for XXX, I would like to be able to use the macro XXX as an object-macro XXX (i.e. ATTR(XXX)), instead of having to add the unnecessary empty parens for the function-like XXX() (i.e. ATTR(XXX())). In this case, adding or removing the parens for XXX() should be completely equivalent in the final expansion.

Question

I would like to know how I can modify the macro ATTR in such a way that it will detect if XXX was passed in with or without parens, and add them to the expansion if they were not provided; i.e. it should process both XXX or XXX() into equivalent results.

Examples

Use Case

The following should be functionally equivalent:

ATTR(XXX)
ATTR(XXX())

// Both expand to: ATTR_IMPL_XXX()

I still need to be able to use the same macro with arguments:

ATTR(XXX(some, args))

// Expands to: ATTR_IMPL_XXX(some, args)

Sample Expansion

As per @EricPostpischil's comment:

#define ATTR(ARG) ATTR_IMPL_##ARG
#define ATTR_IMPL_XXX(...) /* whatever */

ATTR(XXX);
ATTR(XXX());
ATTR(XXX(1, 2, 3));

// Expansion:
// ATTR_IMPL_XXX();
// ATTR_IMPL_XXX();
// ATTR_IMPL_XXX(1, 2, 3);

Clarification

  1. I am not asking if I can use the macro XXX directly with and without parens - I know this is not possible (see function-like macro with the same name as an object-like macro)
  2. Adding more macros/levels of indirection to ATTR (e.g. token pasting to detect no parens) is acceptable
  3. Using a second object-like macro #define XXX_NO_ARGS XXX() with another name is not acceptable
  4. I am not asking how to use variadic macros, or macro arguments.
  5. This is not an X/Y problem, I simply want to know how/if I can use the macro name XXX without parens, as an argument to ATTR
  6. I already have the macro working to translate ATTR(XXX(...)) to ATTR_IMPL_XXX(...), as per (2) I am willing to modify ATTR
10
  • 2
    Seeing "5.", would you accept "No" as an answer (a bad answer of course)? I somehow doubt it... Commented Jan 4 at 7:25
  • @Yunnosch I'd be willing to accept it, but I'm reasonably certain there is an answer. I considered some identifier concatenation magic using a ## b, but couldn't come up with anything myself. Also, I've been going through a lot of CPP libraries (like boost, chaos_pp, cloak, cpp_magic), and seeing what they are able to achieve I find it quite likely that something is entirely within the realm of macro trickery. Commented Jan 4 at 11:54
  • @EricPostpischil I have added Clarification (6): I already have the macro working to translate ATTR(XXX(...)) to ATTR_IMPL_XXX(...), as per (2) I am willing to modify ATTR. Commented Jan 4 at 12:34
  • @EricPostpischil "The token-pasting operator, ##, requires the result be a grammatical token, but ATTR_IMPL_XXX(…) is multiple tokens": I'm not sure what you mean, the token-pasting operator is already working for me: #define ATTR(ARG) ATTR_IMPL_##ARG is what I already have, and It's doing exactly what I said. "The macro ATTR would receive XXX(…) as a single parameter, so it would have no way to process the XXX part separately from the (…) part": See my reply to Yunnosch, there may be a trick using ##, to concat NULL_##XXX(...), and a #define NULL_XXX /* something */ Commented Jan 4 at 12:42
  • 1
    Maybe consider another caller-side use? ATTR(XXX); or ATTR(XXX,1,2,3). Then it would be possible to use C23 __VA_OPT__ to expand the former into some object-like macro and the latter into a function-like one with parameters. But as already mentioned, the specific identifier XXX can't be both an object-like macro and a function-like macro at the same time. Commented Jan 7 at 9:04

2 Answers 2

0

A given macro name is either an object-like macro or a function-like macro at any time. A given macro name cannot be both an object-like macro and a function-like macro at the same time.

If you reference an object-like macro, it gets expanded, but the expansion won't have any arguments (so you get limited value from token pasting). Any parentheses after it are not part of the macro, though they might be scanned as part of a function-like macro (but not the object-like macro you started with) on rescanning.

If you try to reference a function-like macro without an open parenthesis after it, it is not a reference to the function-like macro and, therefore, won't be expanded at all.

So, as intimated by the comments, the answer to the "if" in "Clarification 5" is "no" and the answer to the "how" is "you can't".

The question referenced by "Clarification 1" also quotes the C99 standard (see also C11 §6.10.3.2 Macro replacement ¶2), and that says:

An identifier currently defined as an object-like macro shall not be redefined by another #define preprocessing directive unless the second definition is an object-like macro definition and the two replacement lists are identical. Likewise, an identifier currently defined as a function-like macro shall not be redefined by another #define preprocessing directive unless the second definition is a function-like macro definition that has the same number and spelling of parameters, and the two replacement lists are identical.

The text in C23 §6.10.5.2 Macro replacement ¶2 is the same as in C11. The wording is somewhat different in C90 (and uses two paragraphs instead of one) but the intention is the same.

So, again, the answer is "No, you can't have a single macro name acting as both an object-like macro and a function-like macro".

Sign up to request clarification or add additional context in comments.

2 Comments

I should clarify that I am not asking how to to use XXX directly both with and without parens - I already know I can't. I am already processing it via ATTR, and I would like to know how I can modify ATTR in such a way that it will detect if XXX was passed in with or without parens, and add them to the expansion if they were not provided.
I don’t think it can be done.
0

It is indeed possible, provided you're willing to add something to the end of every ATTR_IMPL_XXX definition:

// Concatenates _REMOVE to the end of a token
#define CONCAT_REMOVE_SUFFIX(...) CONCAT_REMOVE_SUFFIX_HELPER(__VA_ARGS__)
#define CONCAT_REMOVE_SUFFIX_HELPER(...) __VA_ARGS__##_REMOVE

// The key is that'CONSUME_EMPTY_PAREN' and 'CONSUME_EMPTY_PAREN()' both expand
// to just 'CONSUME_EMPTY_PAREN'. We then remove this token by concatenating
// _REMOVE to it, which creates a macro that expands to nothing
#define CONSUME_EMPTY_PAREN() CONSUME_EMPTY_PAREN
#define CONSUME_EMPTY_PAREN_REMOVE // blank


// We add an extra set of parens, then we remove them
#define ATTR(ARG) CONCAT_REMOVE_SUFFIX(ATTR_IMPL_##ARG ())

// Every ATTR_IMPL_XXX definition will need to end with CONSUME_EMPTY_PAREN
#define ATTR_IMPL_XXX(...) WHATEVER(__VA_ARGS__) CONSUME_EMPTY_PAREN

ATTR(XXX)
ATTR(XXX())
ATTR(XXX(1, 2, 3))

This expands to:

WHATEVER()
WHATEVER()
WHATEVER(1, 2, 3)

Try it on Godbolt: https://godbolt.org/z/7Mfsbd67q

If you don't want to modify the ATTR_IMPL_XXX macros in this way, you could also add a set of ATTR_IMPL_HELPER_XXX macros instead:

#define ATTR(ARG) CONCAT_REMOVE_SUFFIX(ATTR_IMPL_HELPER_##ARG ())
#define ATTR_IMPL_HELPER_XXX(...) ATTR_IMPL_XXX(__VA_ARGS__) CONSUME_EMPTY_PAREN
#define ATTR_IMPL_XXX(...) WHATEVER(__VA_ARGS__)

Both of these could be inconvenient if you have a lot of ATTR_IMPL_XXX macros, but as far as I'm aware there's no other way to do it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.