0

I am trying to figure out how to write a preprocessor macro which will "stringify" its argument only if an argument is present

For example:

STRINGIFY(foo) -> "foo"
STRINGIFY() ->

In my very basic example:

#define STRINGIFY(x) #x

Calling STRINGIFY() results in "" rather than nothing at all (which is what I want)

How can this be achieved?

EDIT

As An example of why I want this behavior, I am using the macro to generate an initializer for an array of strings as such

const char* STRINGS[] = {MAP_LIST(STRINGIFY, __VA_ARGS__)}; 

Where MAP_LIST comes from the following project: https://github.com/swansontec/map-macro

If my VA_ARGS lsit has items in it, you end up with, for example, the following:

const char* STRINGS[] = {"a", "b", "c"}; 

But if VA_ARGS is empty, I end up with:

const char* STRINGS[] = {""};

Because, when you "stringify" an empty argument, it gives you an empty string.

Patrick Wright
  • 1,401
  • 7
  • 13
  • 2
    How would you use this in the non-string case? – jarmod Jul 27 '21 at 19:01
  • Why would you call it without an argument? – Barmar Jul 27 '21 at 19:07
  • @Barmar See my edit – Patrick Wright Jul 27 '21 at 19:09
  • 1
    That seems more like a problem with `MAP_LIST()`. It doesn't handle an empty list properly, it calls the function once. – Barmar Jul 27 '21 at 19:10
  • 6
    Um... 0 sized arrays are illegal. You are basically working hard to obtain an invalid declaration. – StoryTeller - Unslander Monica Jul 27 '21 at 19:11
  • @StoryTeller-UnslanderMonica Dang... I didn't even think about that... Perhaps a better approach is in order. – Patrick Wright Jul 27 '21 at 19:15
  • @StoryTeller-UnslanderMonica are they illegal. I think in ANSI-C only – 0___________ Jul 27 '21 at 19:23
  • If a function-like macro is declared to accept an argument, then you literally *cannot* invoke it without one. If there are no tokens between the parentheses in the invocation of such a macro then the argument consists of zero tokens, and stringifying that gets you an empty string. – John Bollinger Jul 27 '21 at 19:40
  • You can take inspiration from P99 FOR implementation (see [here](https://p99.gforge.inria.fr/p99-html/p99__for_8h_source.html#l00084) ): you need only to implement FOR_0 and FOR_1, where FUNC is your stringigicatsion macro. If I have time I will write an example – Koldar Jul 27 '21 at 19:45
  • 2
    @0___________, every version of the C language specification disallows zero-length arrays. Some implementations (notably GCC) accept them as an extension. – John Bollinger Jul 27 '21 at 19:46
  • So what you like instead of `const char* STRINGS[] = {""};` ? `const char* STRINGS[] = ???` – Support Ukraine Jul 27 '21 at 20:22
  • Is this a duplciate of https://stackoverflow.com/questions/21474061/detect-presence-or-absence-of-arguments-in-a-c-macro ? – KamilCuk Jul 27 '21 at 23:52

1 Answers1

1

how to write a preprocessor macro which will "stringify" its argument only if an argument is present

You can do a variadic macro and put a macro with () that will expand to a comma (or specific count of commas) and put __VA_ARGS__ between that macro and () and then overload on the count of arguments.

#define TRIGGER_PARENTHESIS(...)  ,
#define STRINGIFY_0()
#define STRINGIFY_1(a)  #a
#define STRINGIFY_N2(_0,_1,N,...) STRINGIFY_##N
#define STRINGIFY_N(...) STRINGIFY_N2(__VA_ARGS__)
#define STRINGIFY(...) STRINGIFY_N(TRIGGER_PARENTHESIS __VA_ARGS__ (),0,1)(__VA_ARGS__)

See A way to count the number of __VA_ARGS__ arguments, including 0, without compiler specific constructs for a more roboust method.

With a compiler with __VA_OPT__ and in newer C++, it's just:

#define STRINGIFY(...) __VA_OPT__(#__VA_ARGS__)

As noted in the comments, your use case is invalid. If the result of MAP_LIST(STRINGIFY, __VA_ARGS__) would be empty, then it would result in:

const char* STRINGS[] = {}; 

which is invalid C code, because Is an empty initializer list valid C code? and What happens if I define a 0-size array in C/C++? . Both zero sized arrays and empty initializer lists are extensions in GNU C compiler.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111