171

There is a well-known problem with empty args for variadic macros in C99.

example:

#define FOO(...)       printf(__VA_ARGS__)
#define BAR(fmt, ...)  printf(fmt, __VA_ARGS__)

FOO("this works fine");
BAR("this breaks!");

The use of BAR() above is indeed incorrect according to the C99 standard, since it will expand to:

printf("this breaks!",);

Note the trailing comma - not workable.

Some compilers (eg: Visual Studio 2010) will quietly get rid of that trailing comma for you. Other compilers (eg: GCC) support putting ## in front of __VA_ARGS__, like so:

#define BAR(fmt, ...)  printf(fmt, ##__VA_ARGS__)

But is there a standards-compliant way to get this behavior? Perhaps using multiple macros?

Right now, the ## version seems fairly well-supported (at least on my platforms), but I'd really rather use a standards-compliant solution.

Pre-emptive: I know I could just write a small function. I'm trying to do this using macros.

Edit: Here is an example (though simple) of why I would want to use BAR():

#define BAR(fmt, ...)  printf(fmt "\n", ##__VA_ARGS__)

BAR("here is a log message");
BAR("here is a log message with a param: %d", 42);

This automatically adds a newline to my BAR() logging statements, assuming fmt is always a double-quoted C-string. It does NOT print the newline as a separate printf(), which is advantageous if the logging is line-buffered and coming from multiple sources asynchronously.

8
  • 3
    Why use BAR instead of FOO in the first place?
    – GManNickG
    Commented Apr 8, 2011 at 0:20
  • @GMan: I added an example at the end
    – jwd
    Commented Apr 28, 2011 at 17:28
  • 6
    @GMan: Read the last sentence (:
    – jwd
    Commented Apr 28, 2011 at 23:36
  • 7
    This feature has been proposed for inclusion in C2x. Commented Mar 22, 2016 at 13:25
  • 3
    @zwol the latest version submitted to WG14 looks like this, which uses a new syntax based on the __VA_OPT__ keyword. This has already been "adopted" by C++, so I expect C will follow suit. (don't know whether that means it was fast-tracked into C++17 or if it's set for C++20 though) Commented Aug 9, 2017 at 17:25

12 Answers 12

122

There is an argument counting trick that you can use.

Here is one standard-compliant way to implement the second BAR() example in jwd's question:

#include <stdio.h>

#define BAR(...) printf(FIRST(__VA_ARGS__) "\n" REST(__VA_ARGS__))

/* expands to the first argument */
#define FIRST(...) FIRST_HELPER(__VA_ARGS__, throwaway)
#define FIRST_HELPER(first, ...) first

/*
 * if there's only one argument, expands to nothing.  if there is more
 * than one argument, expands to a comma followed by everything but
 * the first argument.  only supports up to 9 arguments but can be
 * trivially expanded.
 */
#define REST(...) REST_HELPER(NUM(__VA_ARGS__), __VA_ARGS__)
#define REST_HELPER(qty, ...) REST_HELPER2(qty, __VA_ARGS__)
#define REST_HELPER2(qty, ...) REST_HELPER_##qty(__VA_ARGS__)
#define REST_HELPER_ONE(first)
#define REST_HELPER_TWOORMORE(first, ...) , __VA_ARGS__
#define NUM(...) \
    SELECT_10TH(__VA_ARGS__, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\
                TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, ONE, throwaway)
#define SELECT_10TH(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, ...) a10

int
main(int argc, char *argv[])
{
    BAR("first test");
    BAR("second test: %s", "a string");
    return 0;
}

This same trick is used to:

Explanation

The strategy is to separate __VA_ARGS__ into the first argument and the rest (if any). This makes it possible to insert stuff after the first argument but before the second (if present).

FIRST()

This macro simply expands to the first argument, discarding the rest.

The implementation is straightforward. The throwaway argument ensures that FIRST_HELPER() gets two arguments, which is required because the ... needs at least one. With one argument, it expands as follows:

  1. FIRST(firstarg)
  2. FIRST_HELPER(firstarg, throwaway)
  3. firstarg

With two or more, it expands as follows:

  1. FIRST(firstarg, secondarg, thirdarg)
  2. FIRST_HELPER(firstarg, secondarg, thirdarg, throwaway)
  3. firstarg

REST()

This macro expands to everything but the first argument (including the comma after the first argument, if there is more than one argument).

The implementation of this macro is far more complicated. The general strategy is to count the number of arguments (one or more than one) and then expand to either REST_HELPER_ONE() (if only one argument given) or REST_HELPER_TWOORMORE() (if two or more arguments given). REST_HELPER_ONE() simply expands to nothing -- there are no arguments after the first, so the remaining arguments is the empty set. REST_HELPER_TWOORMORE() is also straightforward -- it expands to a comma followed by everything except the first argument.

The arguments are counted using the NUM() macro. This macro expands to ONE if only one argument is given, TWOORMORE if between two and nine arguments are given, and breaks if 10 or more arguments are given (because it expands to the 10th argument).

The NUM() macro uses the SELECT_10TH() macro to determine the number of arguments. As its name implies, SELECT_10TH() simply expands to its 10th argument. Because of the ellipsis, SELECT_10TH() needs to be passed at least 11 arguments (the standard says that there must be at least one argument for the ellipsis). This is why NUM() passes throwaway as the last argument (without it, passing one argument to NUM() would result in only 10 arguments being passed to SELECT_10TH(), which would violate the standard).

Selection of either REST_HELPER_ONE() or REST_HELPER_TWOORMORE() is done by concatenating REST_HELPER_ with the expansion of NUM(__VA_ARGS__) in REST_HELPER2(). Note that the purpose of REST_HELPER() is to ensure that NUM(__VA_ARGS__) is fully expanded before being concatenated with REST_HELPER_.

Expansion with one argument goes as follows:

  1. REST(firstarg)
  2. REST_HELPER(NUM(firstarg), firstarg)
  3. REST_HELPER2(SELECT_10TH(firstarg, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, ONE, throwaway), firstarg)
  4. REST_HELPER2(ONE, firstarg)
  5. REST_HELPER_ONE(firstarg)
  6. (empty)

Expansion with two or more arguments goes as follows:

  1. REST(firstarg, secondarg, thirdarg)
  2. REST_HELPER(NUM(firstarg, secondarg, thirdarg), firstarg, secondarg, thirdarg)
  3. REST_HELPER2(SELECT_10TH(firstarg, secondarg, thirdarg, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, ONE, throwaway), firstarg, secondarg, thirdarg)
  4. REST_HELPER2(TWOORMORE, firstarg, secondarg, thirdarg)
  5. REST_HELPER_TWOORMORE(firstarg, secondarg, thirdarg)
  6. , secondarg, thirdarg
3
  • 2
    Note that this will fail if you call BAR with 10 or more arguments, and though it's relatively easy to extend to more arguments, it will always have a upper bound on the number of arguments it can deal with
    – Chris Dodd
    Commented Apr 19, 2013 at 22:12
  • 2
    @ChrisDodd: Correct. Unfortunately, there doesn't appear to be a way to avoid a limit in the number of arguments without relying on compiler-specific extensions. Also, I'm unaware of a way to reliably test if there are too many arguments (so that a useful compiler error message can be printed, rather than a strange failure). Commented Apr 20, 2013 at 1:25
  • Unfortunately, it works only with string arguments! BAR("val:%d", 1); fails to compile!
    – Alex D
    Commented Oct 6, 2021 at 23:27
71

It is possible to avoid the use of GCC's ,##__VA_ARGS__ extension if you are willing to accept some hardcoded upper limit on the number of arguments you can pass to your variadic macro, as described in Richard Hansen's answer to this question. If you do not want to have any such limit, however, to the best of my knowledge it is not possible using only C99-specified preprocessor features; you must use some extension to the language. clang and icc have adopted this GCC extension, but MSVC has not.

Back in 2001 I wrote up the GCC extension for standardization (and the related extension that lets you use a name other than __VA_ARGS__ for the rest-parameter) in document N976, but that received no response whatsoever from the committee; I don't even know if anyone read it. In 2016 it was proposed again in N2023, and I encourage anyone who knows how that proposal is going to let us know in the comments.

7
  • 2
    Judging by my disability to find a solution on the web and the lack of answers here, I guess you're right ):
    – jwd
    Commented Apr 11, 2011 at 16:27
  • 2
    Is n976 what you are referring to? I searched the rest of the C working group's documents for a response but never found one. It wasn't even in the agenda for the subsequent meeting. The only other hit on this topic was Norway's comment #4 in n868 back from before C99 was ratified (again with no follow-up discussion). Commented Feb 8, 2012 at 21:33
  • 4
    Yes, specifically the second half of that. There may have been discussion on comp.std.c but I was unable to find any in Google Groups just now; it certainly never got any attention from the actual committee (or if it did, nobody ever told me about it).
    – zwol
    Commented Feb 8, 2012 at 23:03
  • 1
    I'm afraid I don't have a proof, nor am I anymore the right person to try to think one up. I did write half of GCC's preprocessor, but that was more than ten years ago, and I'd never have thought of the argument-counting trick below, even then.
    – zwol
    Commented Jan 31, 2014 at 16:07
  • 8
    This extension works with clang & intel icc compilers, as well as gcc.
    – ACyclic
    Commented Dec 17, 2015 at 15:25
20

Not a general solution, but in the case of printf you could append a newline like:

#define BAR_HELPER(fmt, ...) printf(fmt "\n%s", __VA_ARGS__)
#define BAR(...) BAR_HELPER(__VA_ARGS__, "")

I believe it ignores any extra args that aren't referenced in the format string. So you could probably even get away with:

#define BAR_HELPER(fmt, ...) printf(fmt "\n", __VA_ARGS__)
#define BAR(...) BAR_HELPER(__VA_ARGS__, 0)

I can't believe C99 was approved without a standard way to do this. AFAICT the problem exists in C++11 too.

2
  • the problem with this extra 0 is that it will actually end up in the code if it calls vararg function. Check for solution provided by Richard Hansen
    – Pavel P
    Commented Jul 6, 2012 at 23:55
  • 1
    @Pavel is correct about the second example, but the first works great. +1. Commented Dec 13, 2014 at 20:47
11

There is a way to handle this specific case using something like Boost.Preprocessor. You can use BOOST_PP_VARIADIC_SIZE to check the size of the argument list, and then conditionaly expand to another macro. The one shortcoming of this is that it can't distinguish between 0 and 1 argument, and the reason for this becomes clear once you consider the following:

BOOST_PP_VARIADIC_SIZE()      // expands to 1
BOOST_PP_VARIADIC_SIZE(,)     // expands to 2
BOOST_PP_VARIADIC_SIZE(,,)    // expands to 3
BOOST_PP_VARIADIC_SIZE(a)     // expands to 1
BOOST_PP_VARIADIC_SIZE(a,)    // expands to 2
BOOST_PP_VARIADIC_SIZE(,b)    // expands to 2
BOOST_PP_VARIADIC_SIZE(a,b)   // expands to 2
BOOST_PP_VARIADIC_SIZE(a, ,c) // expands to 3

The empty macro argument list actually consists of one argument that happens to be empty.

In this case, we are lucky since your desired macro always has at least 1 argument, we can implement it as two "overload" macros:

#define BAR_0(fmt) printf(fmt "\n")
#define BAR_1(fmt, ...) printf(fmt "\n", __VA_ARGS__)

And then another macro to switch between them, such as:

#define BAR(...) \
    BOOST_PP_CAT(BAR_, BOOST_PP_GREATER(
        BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 1))(__VA_ARGS__) \
    /**/

or

#define BAR(...) BOOST_PP_IIF( \
    BOOST_PP_GREATER(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 1), \
        BAR_1, BAR_0)(__VA_ARGS__) \
    /**/

Whichever you find more readable (I prefer the first as it gives you a general form for overloading macros on the number of arguments).

It is also possible to do this with a single macro by accessing and mutating the variable arguments list, but it is way less readable, and is very specific to this problem:

#define BAR(...) printf( \
    BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__) "\n" \
    BOOST_PP_COMMA_IF( \
        BOOST_PP_GREATER(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 1)) \
    BOOST_PP_ARRAY_ENUM(BOOST_PP_ARRAY_POP_FRONT( \
        BOOST_PP_VARIADIC_TO_ARRAY(__VA_ARGS__)))) \
    /**/

Also, why is there no BOOST_PP_ARRAY_ENUM_TRAILING? It would make this solution much less horrible.

Edit: Alright, here is a BOOST_PP_ARRAY_ENUM_TRAILING, and a version that uses it (this is now my favourite solution):

#define BOOST_PP_ARRAY_ENUM_TRAILING(array) \
    BOOST_PP_COMMA_IF(BOOST_PP_ARRAY_SIZE(array)) BOOST_PP_ARRAY_ENUM(array) \
    /**/

#define BAR(...) printf( \
    BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__) "\n" \
    BOOST_PP_ARRAY_ENUM_TRAILING(BOOST_PP_ARRAY_POP_FRONT( \
        BOOST_PP_VARIADIC_TO_ARRAY(__VA_ARGS__)))) \
    /**/
3
  • 1
    Nice to learn about Boost.Preprocessor, +1. Note that BOOST_PP_VARIADIC_SIZE() uses the same argument counting trick I documented in my answer, and has the same limitation (it will break if you pass more than a certain number of arguments). Commented Apr 19, 2013 at 21:56
  • 1
    Yep, I saw that your approach was the same one used by Boost, but the boost solution is very well maintained, and has a lot of other really useful features for use when developing more sophisticated macros. The recursion stuff is particularly cool (and used behind the scenes in the last approach that uses BOOST_PP_ARRAY_ENUM).
    – DRayX
    Commented Apr 19, 2013 at 22:00
  • 1
    A Boost answer that actually applies to the c tag! Hooray!
    – Justin
    Commented Oct 30, 2014 at 15:28
11

A very simple macro I'm using for debug printing:

#define DBG__INT(fmt, ...) printf(fmt "%s", __VA_ARGS__);
#define DBG(...) DBG__INT(__VA_ARGS__, "\n")

int main() {
        DBG("No warning here");
        DBG("and we can add as many arguments as needed. %s", "nice!");
        return 0;
}

No matter how many arguments are passed to DBG there are no c99 warning.

The trick is DBG__INT adding a dummy param so ... will always have at least one argument and c99 is satisfied.

4
  • 1
    A small warning about this code, as written: the double-underscore in __DBG_INT is considered to be something that results in "undefined behavior". It's something that's unlikely to cause problems, but it's good to know about when writing things from scratch or refactoring--situations where it's easy to choose a different convention like DBG_INT_ or DBG__INT.
    – chadjoan
    Commented Nov 9, 2020 at 6:05
  • 1
    Relevant snippets from the C11 standard (N1570), 7.1.3 Reserved identifiers: "1. All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use." "2. No other identifiers are reserved. If the program declares or defines an identifier in a context in which it is reserved (other than as allowed by 7.1.4), or defines a reserved identifier as a macro name, the behavior is undefined." (Note: this also rules out something like _DBG_INT.)
    – chadjoan
    Commented Nov 9, 2020 at 6:06
  • Didn't know that, thanks. Is it considered undefined behavior also for C99?
    – SimonW
    Commented Nov 9, 2020 at 22:09
  • 1
    You're welcome; I hope it helps. And yep, it is UB in C99 as well. It's the same section (7.1.3, p1 and p2) in C99/N1256. Best of luck!
    – chadjoan
    Commented Nov 12, 2020 at 9:03
5

I ran into a similar problem recently, and I do believe there's a solution.

The key idea is that there's a way to write a macro NUM_ARGS to count the number of arguments which a variadic macro is given. You can use a variation of NUM_ARGS to build NUM_ARGS_CEILING2, which can tell you whether a variadic macro is given 1 argument or 2-or-more arguments. Then you can write your Bar macro so that it uses NUM_ARGS_CEILING2 and CONCAT to send its arguments to one of two helper macros: one which expects exactly 1 argument, and another which expects a variable number of arguments greater than 1.

Here's an example where I use this trick to write the macro UNIMPLEMENTED, which is very similar to BAR:

STEP 1:

/** 
 * A variadic macro which counts the number of arguments which it is
 * passed. Or, more precisely, it counts the number of commas which it is
 * passed, plus one.
 *
 * Danger: It can't count higher than 20. If it's given 0 arguments, then it
 * will evaluate to 1, rather than to 0.
 */

#define NUM_ARGS(...)                                                   \
    NUM_ARGS_COUNTER(__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13,       \
                     12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)    

#define NUM_ARGS_COUNTER(a1, a2, a3, a4, a5, a6, a7,        \
                         a8, a9, a10, a11, a12, a13,        \
                         a14, a15, a16, a17, a18, a19, a20, \
                         N, ...)                            \
    N

STEP 1.5:

/*
 * A variant of NUM_ARGS that evaluates to 1 if given 1 or 0 args, or
 * evaluates to 2 if given more than 1 arg. Behavior is nasty and undefined if
 * it's given more than 20 args.
 */

#define NUM_ARGS_CEIL2(...)                                           \
    NUM_ARGS_COUNTER(__VA_ARGS__, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, \
                     2, 2, 2, 2, 2, 2, 2, 1)

Step 2:

#define _UNIMPLEMENTED1(msg)                                        \
    log("My creator has forsaken me. %s:%s:%d." msg, __FILE__,      \
        __func__, __LINE__)

#define _UNIMPLEMENTED2(msg, ...)                                   \
    log("My creator has forsaken me. %s:%s:%d." msg, __FILE__,      \
        __func__, __LINE__, __VA_ARGS__)

STEP 3:

#define UNIMPLEMENTED(...)                                              \
    CONCAT(_UNIMPLEMENTED, NUM_ARGS_CEIL2(__VA_ARGS__))(__VA_ARGS__)

Where CONCAT is implemented in the usual way. As a quick hint, if the above seems confusing: the goal of CONCAT there is to expand to another macro "call".

Note that NUM_ARGS itself isn't used. I just included it to illustrate the basic trick here. See Jens Gustedt's P99 blog for a nice treatment of it.

Two notes:

  • NUM_ARGS is limited in the number of arguments that it handles. Mine can only handle up to 20, although the number is totally arbitrary.

  • NUM_ARGS, as shown, has a pitfall in that it returns 1 when given 0 arguments. The gist of it is that NUM_ARGS is technically counting [commas + 1], and not args. In this particular case, it actually works to our advantage. _UNIMPLEMENTED1 will handle an empty token just fine and it saves us from having to write _UNIMPLEMENTED0. Gustedt has a workaround for that as well, although I haven't used it and I'm not sure if it would work for what we're doing here.

6
  • +1 for bringing up the argument counting trick, -1 for being really hard to follow Commented Jun 23, 2012 at 20:19
  • The comments you added were an improvement, but there are still a number of issues: 1. You discuss and define NUM_ARGS but don't use it. 2. What is the purpose of UNIMPLEMENTED? 3. You never solve the example problem in the question. 4. Walking through the expansion one step at a time would illustrate how it works and explain the role of each helper macro. 5. Discussing 0 arguments is distracting; the OP was asking about standards compliance, and 0 arguments is forbidden (C99 6.10.3p4). 6. Step 1.5? Why not step 2? 7. "Steps" implies actions that occur sequentially; this is just code. Commented Jun 23, 2012 at 21:53
  • 8. You link to the whole blog, not the relevant post. I couldn't find the post you were referring to. 9. The last paragraph is awkward: This method is obscure; that's why nobody else had posted a correct solution before. Also, if it works and adheres to the standard, Zack's answer must be wrong. 10. You should define CONCAT() -- don't assume readers know how it works. Commented Jun 23, 2012 at 22:09
  • (Please don't interpret this feedback as an attack -- I really did want to upvote your answer but didn't feel comfortable doing so unless it was made easier to understand. If you can improve the clarity of your answer, I'll upvote yours and delete mine.) Commented Jun 23, 2012 at 22:12
  • 2
    I would never have thought of this approach, and I wrote roughly half of GCC's current preprocessor! That said, I do still say that "there is no standard way to get this effect" because both your and Richard's techniques impose an upper limit on the number of arguments to the macro.
    – zwol
    Commented Mar 24, 2013 at 0:41
5

If you are using gcc 8+, clang 6+ or MSVC 2019 (source), then you can also use the (newer) __VA_OPT__ macro, which conditionally expands if __VA_ARGS__ is non-empty.

So, we can convert the two FOO and BAR macros into one:

#define FOO(s, ...) printf(s __VA_OPT__(,) __VA_ARGS__)

and so, FOO("hello!") will expand to printf("hello!"), and FOO("x = %d", 5) will expand to printf("x = %d", 5).

This is a relatively new feature (introduced in C++2a) so your compiler might not support it yet.

3

This is the simplified version that I use. It is based upon the great techniques of the other answers here, so many props to them:

#define _SELECT(PREFIX,_5,_4,_3,_2,_1,SUFFIX,...) PREFIX ## _ ## SUFFIX

#define _BAR_1(fmt)      printf(fmt "\n")
#define _BAR_N(fmt, ...) printf(fmt "\n", __VA_ARGS__);
#define BAR(...) _SELECT(_BAR,__VA_ARGS__,N,N,N,N,1)(__VA_ARGS__)

int main(int argc, char *argv[]) {
    BAR("here is a log message");
    BAR("here is a log message with a param: %d", 42);
    return 0;
}

That's it.

As with other solutions this is limited to the number of arguments the macro. To support more, add more parameters to _SELECT, and more N arguments. The argument names count down (instead of up) to serve as a reminder that the count-based SUFFIX argument is provided in reverse order.

This solution treats 0 arguments as though it is 1 argument. So BAR() nominally "works", because it expands to _SELECT(_BAR,,N,N,N,N,1)(), which expands to _BAR_1()(), which expands to printf("\n").

If you want, you can get creative with the use of _SELECT and provide different macros for different number of arguments. For example, here we have a LOG macro that takes a 'level' argument before the format. If format is missing, it logs "(no message)", if there is just 1 argument, it will log it through "%s", otherwise it will treat the format argument as a printf format string for the remaining arguments.

#define _LOG_1(lvl)          printf("[%s] (no message)\n", #lvl)
#define _LOG_2(lvl,fmt)      printf("[%s] %s\n", #lvl, fmt)
#define _LOG_N(lvl,fmt, ...) printf("[%s] " fmt "\n", #lvl, __VA_ARGS__)
#define LOG(...) _SELECT(_LOG,__VA_ARGS__,N,N,N,2,1)(__VA_ARGS__)

int main(int argc, char *argv[]) {
    LOG(INFO);
    LOG(DEBUG, "here is a log message");
    LOG(WARN, "here is a log message with param: %d", 42);
    return 0;
}
/* outputs:
[INFO] (no message)
[DEBUG] here is a log message
[WARN] here is a log message with param: 42
*/
1
  • This still triggers a warning when compiled with -pedantic. Commented Nov 11, 2018 at 11:53
2

if c++11 or above is available, and the macro is intended to be expanded to a function call, you can make a wrapper for it, for example:
#define BAR(fmt, ...) printf(fmt, __VA_ARGS__)
can be converted to
#define BAR(fmt, ...) BAR_wrapper(fmt)(__VA_ARGS__)
where BAR_wrapper can be defined as:

struct BAR_wrapper_t {
  BAR_wrapper_t(const char* fmt) : fmt(fmt) {}
  const char* fmt;
  int operator()() const { return printf(fmt); }
  template <typename... Args>
  int operator()(Args&& args) const { return printf(fmt, std::forward<Args>(args)...); }
};
inline BAR_wrapper_t BAR_wrapper(const char* fmt) { return BAR_wrapper_t(fmt); }
1

In your situation (at least 1 argument present, never 0), you can define BAR as BAR(...), use Jens Gustedt's HAS_COMMA(...) to detect a comma and then dispatch to BAR0(Fmt) or BAR1(Fmt,...) accordingly.

This:

#define HAS_COMMA(...) HAS_COMMA_16__(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0)
#define HAS_COMMA_16__(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15
#define CAT_(X,Y) X##Y
#define CAT(X,Y) CAT_(X,Y)
#define BAR(.../*All*/) CAT(BAR,HAS_COMMA(__VA_ARGS__))(__VA_ARGS__)
#define BAR0(X) printf(X "\n")
#define BAR1(X,...) printf(X "\n",__VA_ARGS__)


#include <stdio.h>
int main()
{
    BAR("here is a log message");
    BAR("here is a log message with a param: %d", 42);
}

compiles with -pedantic without a warning.

0

C (gcc), 762 bytes

#define EMPTYFIRST(x,...) A x (B)
#define A(x) x()
#define B() ,

#define EMPTY(...) C(EMPTYFIRST(__VA_ARGS__) SINGLE(__VA_ARGS__))
#define C(...) D(__VA_ARGS__)
#define D(x,...) __VA_ARGS__

#define SINGLE(...) E(__VA_ARGS__, B)
#define E(x,y,...) C(y(),)

#define NONEMPTY(...) F(EMPTY(__VA_ARGS__) D, B)
#define F(...) G(__VA_ARGS__)
#define G(x,y,...) y()

#define STRINGIFY(...) STRINGIFY2(__VA_ARGS__)
#define STRINGIFY2(...) #__VA_ARGS__

#define BAR(fmt, ...) printf(fmt "\n" NONEMPTY(__VA_ARGS__) __VA_ARGS__)

int main() {
    puts(STRINGIFY(NONEMPTY()));
    puts(STRINGIFY(NONEMPTY(1)));
    puts(STRINGIFY(NONEMPTY(,2)));
    puts(STRINGIFY(NONEMPTY(1,2)));

    BAR("here is a log message");
    BAR("here is a log message with a param: %d", 42);
}

Try it online!

Assumes:

  • No arg contain comma or bracket
  • No arg contain A~G (can rename to hard_collide ones)
1
  • The no arg contain comma limitation may be bypassed by checking multi after some more passes, but no bracket still there
    – l4m2
    Commented Mar 16, 2019 at 19:57
-2

The standard solution is to use FOO instead of BAR. There are a few weird cases of argument reordering it probably can't do for you (though I bet someone can come up with clever hacks to disassemble and reassemble __VA_ARGS__ conditionally based on the number of arguments in it!) but in general using FOO "usually" just works.

3
  • 1
    The question was "is there a standards-compliant way to get this behavior?"
    – Marsh Ray
    Commented Dec 29, 2011 at 19:50
  • 2
    And the question has included a rationale for not using FOO for ages now. Commented Nov 7, 2014 at 8:34
  • How is your answer helping in any way?
    – Can
    Commented Feb 26, 2023 at 11:32

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.