3

After a bit of research and a lot of mucking about I've come up with this:

#include <stdio.h>

// Variadic Function Overloading:

#define VOID    "__variadic_VOID__"

#define variadic_count(...) variadic_count_(__VA_ARGS__, 6, 5, 4, 3, 2, 1, 0)
#define variadic_count_(_6, _5, _4, _3, _2, _1, count, ...) count

#define variadic_token(func, ...)   variadic_token_(func, variadic_count(__VA_ARGS__))
#define variadic_token_(func, count)    variadic_token__(func, count)
#define variadic_token__(func, count)   func ## _ ## count

#define variadic(func, ...)\
do {\
    if (#__VA_ARGS__ == "\"__variadic_VOID__\"")\
    {\
        variadic_token__(func, 0)();\
    }\
    else\
    {\
        variadic_token(func, __VA_ARGS__)(__VA_ARGS__);\
    }\
} while (0)

// Usage:

#define somefunction(...)   variadic(somefunction, __VA_ARGS__)

#define somefunction_0()\
do {\
    printf("somefunction_0(VOID)\n\n");\
} while (0)

#define somefunction_1(x)\
do {\
    printf("somefunction_1(x = %i)\n\n", (x));\
} while (0)

#define somefunction_2(x, y)\
do {\
    printf("somefunction_2(x = %i, y = %i)\n\n", (x), (y));\
} while (0)

int main(int argc, char* argv[])
{
    //somefunction();   ERROR
    somefunction(VOID);
    somefunction(1);
    somefunction(11);
    somefunction(2, 3);
    //somefunction(1, 2, 3);    ERROR

    printf("\n\n");
    return 0;
}

Essentially, it allows for a distinction between zero and one arguments via the special token VOID. It works unless the variadic function/macro is passed the string literal defined by VOID as a sole argument. In this case, only a call of somefunction("__variadic_VOID__"); will cause unexpected behavior; passing a variable with the same value as the string literal does not cause unexpected behavior.

While the provided code only works for 0-6 arguments, it can be modified to work with a greater number of arguments.

I'm curious, however, as to whether or not if ("blah" == "blah") {doSomething();} is optimized to doSomething(); by the compiler (Pelles C)? Or does the pointer comparison occur at runtime? If it is optimized away, then I think this code snippet allows for easy and efficient variadic overloading of functions/macros...is that right?

sweetname
  • 69
  • 1
  • 2
  • 7
  • 3
    You can't use `==` to compare strings, it will compare the *pointers* to the strings and not the actual strings. – Some programmer dude Jan 26 '15 at 15:26
  • @JoachimPileborg I've tested the code I provided above and it works; in this case the comparison is between two string literals. – sweetname Jan 26 '15 at 15:41
  • 3
    Equal string literals may share storage, but don't have to. If two strings compare equal with `==`, they are equal with `strcmp`, but it's not true the other way round. E.g., in `char foo[] = "blah"; foo == "blah";`, the comparison is guaranteed to be false. – mafso Jan 26 '15 at 15:51
  • @mafso Again, the code works as is...in this particular case, `==` seems sufficient. – sweetname Jan 26 '15 at 16:02
  • 3
    Don't trust in what seems to work in C :) It's unspecified and may change for each string ,or with the next compiler update. Maybe you can do `if ("blah" == "blah" || strcmp("blah", "blah")) { doSomething(); }`, which probably gets optimized out in most cases, and otherwise, a safe fallback is used. (Gcc can even optimize `strcmp`, I guess, when passed literals.) – mafso Jan 26 '15 at 16:22
  • 2
    It may **seem** sufficient, but it isn't. Identical string literals within a program *may or may not* share storage. Any code that assumes they do (or that they don't) is non-portable, and in accordance with Murphy's Law will work perfectly until it breaks catastrophically during an important demo. – Keith Thompson Jan 26 '15 at 16:58
  • Stupid typo: `if("blah" == "blah" || !strcmp("blah", "blah")) { doSomething(); }` -- `strcmp` returns 0 if the strings are equal. (Just in case someone uses it without noticing, that would be a subtle bug which probably wouldn't show up for a long time...) – mafso Jan 26 '15 at 17:11
  • Well, at any rate it certainly appears as if the Pelles C compiler I'm using forces the string literals to share storage and I've just read that some other compilers have options to allow the same. Portability aside, I'd still like a definitive answer concerning the optimization... – sweetname Jan 26 '15 at 20:59

1 Answers1

0

A much better scheme to use is:

#define variadic(func, ...)\
do {\
    if (0 == *#__VA_ARGS__)\
    {\
        variadic_token__(func, 0)();\
    }\
    else\
    {\
        variadic_token(func, __VA_ARGS__)(__VA_ARGS__);\
    }\
} while (0)

Then a call like somefunction() will expand to a call to somefunction_0() and there's no need for the magic VOID token. Its also much safer and more likely to be optimized away by the compiler.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • What does the asterisk accomplish? Also, when I substituted your definition for mine and called `somefunction();` I receive an "invalid expression" error... – sweetname Jan 26 '15 at 20:48
  • @sweetname: It gets the first character of the string literal -- that will be a NUL character if and only if the __VA_ARGS__ is empty – Chris Dodd Jan 26 '15 at 23:38
  • The problem is that if `__VA_ARGS__` is empty, then the call to `variadic_token(func, __VA_ARGS__)(__VA_ARGS__);` fails because `variadic_token` has a `...` which requires at least one token. Even though this code is unreachable, it still throws an error upon compilation. – sweetname Jan 27 '15 at 01:20