4

For example I want to write my own printf() alternative, but I have to perform calculations on the variable arguments:

#define log(fmt_string, ...) my_log(fmt_string, pack_args(__VA_ARGS__), __VA_ARGS__)

where pack_args(...) - is a macro too. How should I change this code to handle the only fmt_string presence scenario?

log("Some message here");
Anton Kochkov
  • 1,117
  • 1
  • 9
  • 25
  • Not directly related to your question, but this macro will process arguments twice. for example, `log("foo %d", func());` will call func twice. To fix this you could make a function e.g. `my_log_pack(char const *, ...);` which processes its parameter list twice. – M.M Sep 24 '15 at 00:36
  • I did not fully understand your problem. Your example is too minimal. How this **`pack_args`** macro is defined? How is defined **`my_log`** function? May be you can take a lock on: [How to easily create fully “variadic” functions with C++ 98 standard?](https://stackoverflow.com/questions/59331919/how-to-easily-create-fully-variadic-functions-with-c-98-standard/). – Evandro Coan Dec 15 '19 at 00:14

2 Answers2

4

In P99 I have two macros

#define P00_ARG(                                               \
 _1, _2, _3, _4, _5, _6, _7, _8,                               \
 _9, _10, _11, _12, _13, _14, _15, _16,                        \
  ... etc ...                                                  \
 _153, _154, _155, _156, _157, _158, _159,                     \
 ...) _159
#define P99_HAS_COMMA(...) P00_ARG(__VA_ARGS__,                \
 1, 1, 1, 1, 1, 1, 1,                                          \
 1, 1, 1, 1, 1, 1, 1, 1,                                       \
  ... etc ....                                                 \
 1, 1, 1, 1, 1, 1, 0, 0)

You can use this to determine if your argument has a comma (so there are more arguments than your format) or not (only a format). You can then use that to construct a call to one of two macros:

#define log(...) log2(P99_HAS_COMMA(__VA_ARGS__), __VA_ARGS__)
#define log2(N, ...) log3(N, __VA_ARGS__)
#define log3(N, ...) log ## N(__VA_ARGS__)

#define log0(FMT)              /* your version with format only goes here */
#define log1(FMT, __VA_ARGS__) /* your version with more goes here */
Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
1

How should I change this code to [handle] the only fmt_string presence scenario?

You cannot do this at all with a variadic macro in standard C. The standard explicitly specifies that in the invocation of a variadic macro "there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...)" (C2011, 6.10.3/4). You could allow the macro to be used with just one argument by changing it to ...

#define log(...) /* ... */

... but then you could not separate the format string from the other arguments -- at least not without re-introducing the same problem you have now.

You'll need to use a bona fide function if you need to support a zero-length variable argument list.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • 2
    gcc actually **can** remove a leading comma for `__VA_ARGS__`. IIRC just prefix with `##` (better verify in the documentation), as an extension. – too honest for this site Sep 23 '15 at 19:41
  • 2
    @Olaf, you are correct that *as extensions*, GCC permits a variadic macro to be invoked with zero variable arguments, and handles a `##` operator appearing between a comma and `__VA_ARGS__` by suppressing the comma. Perhaps that will serve the OP, but I am rarely inclined to recommend incorporating dependencies on behavior specific to a particular compiler. – John Bollinger Sep 23 '15 at 20:10
  • Well, I am a bit split here. In general, I do not like relying on compiler features. OTOH, I see gcc as being OSS and (one of) the most used compilers some kind of exception (I might include clang/llvm if I knew them better). This because you do not depend on a specific vendor, but can port it yourself - if required. It is also verrrrry unlikely to suddenly disappear. Anyway, I think we should leave the decission to OP, just providing the information. Isn't that what freedom is about? – too honest for this site Sep 23 '15 at 20:28
  • @Olaf, I love `gcc` and get a lot of use out of it, but my concern is not usually whether one can find a suitable compiler with which to build my code. I'm more often concerned with whether one can build it with whatever compiler one wants (or needs) to use. – John Bollinger Sep 23 '15 at 20:38
  • @Olaf, nevertheless, I updated the answer to qualify the "you cannot do this at all" assertion. – John Bollinger Sep 23 '15 at 20:39
  • @Olaf I know about `##__VA_ARGS__` extension, but it would would only in case of `#define log(fmt, ...) my_log(fmt, ##__VA_ARGS__)`. But question is more how to deal with that `pack_args(__VA_ARGS__)` macro in case of the missing arguments and comma before. – Anton Kochkov Sep 23 '15 at 21:26
  • @AntonKochkov: As you do not show the macro and what you actually intend, there is no way to answer other than John did. Without further information, it is not possible to provide a possible alternative (if any) to your approach. – too honest for this site Sep 23 '15 at 23:33
  • @Olaf well, this one is quite complex, but basically it looks like `#define pack_args(...) APPLY_ALL(another_macro, __VA_ARGS__)` from this article http://ptspts.blogspot.ru/2013/11/how-to-apply-macro-to-all-arguments-of.html – Anton Kochkov Sep 23 '15 at 23:42
  • "Not all" is just exagerated. You can do a lot with variable argument macros, and if you are willing to accept that your `log` macro will not have more that, say, 158 arguments, this can be done. – Jens Gustedt Sep 23 '15 at 23:43
  • @JensGustedt, I'd say "strict" rather than "exaggerated". I think my answer is correct for the question as posed, without additional provisions or constraints. With that said, a constraint such as you propose is unlikely to present any problem in practice: +1. – John Bollinger Sep 24 '15 at 02:06