0

I have two functions foo1(a,b) & foo2(a,b,c) and a macro

#define add(a,b) foo(a,b)

I need to re-define macro to accomplish,

1.if add() is called with 2 parameters, then call foo1

  1. if add() is called with 3 parameters then call foo2

Im new to the option VA_ARGS. How can I do that

Sruthy
  • 27
  • 1
  • 9
  • 1
    Why can't `add` be recoded as a function that takes a variable argument list? – Bathsheba Dec 06 '17 at 12:48
  • 1
    ... Or to reduce the chance of errors, a function that takes 2 mandatory parameters and another optional one by a variable argument list. – StoryTeller - Unslander Monica Dec 06 '17 at 12:49
  • The _sane_ solution: a function `type add (type a, type b, type c);` where `c` is optional and can be set to NULL or some such. Variadic macros is often the road towards madness. – Lundin Dec 06 '17 at 15:00

3 Answers3

5

If you just want to distinguish between two functions, the following works:

#define ADD(_1, _2, _3, X, ...) X
#define add(...) ADD(__VA_ARGS__, add3, add2, 0)(__VA_ARGS__)

The auxiliary macro ADD always picks the fourth argument:

add(a, b)    --> ADD(a, b, add3, add2, 0)    --> add2
add(a, b, c) --> ADD(a, b, c, add3, add2, 0) --> add3

The drawback is that you get quite cryptic error messages when you don't supply two or three arguments to the function.

The advantage over variadic functions is that you get type safety. For example if your functions operate on doubles, you can still say add(1, 2) and the integer arguments will be converted to doubles. And variadic functions require some additional information on the number of actual arguments, so that's not a feasible solution here, unless you specify the number of summands in the function.

Addendum: I've changed the add macro so that it doesn't pass an empty variadic list to ADD. Some compilers allow empty lists, but it isn't standard C.

M Oehm
  • 28,726
  • 3
  • 31
  • 42
  • 2
    Yours is way more readable than my shot at it, I must say. Definite +1 – StoryTeller - Unslander Monica Dec 06 '17 at 13:15
  • I don't believe `#define ADD(_1, _2, _3, X, ...) X` is standard C, you have to burn the `__VA_ARGS__` somewhere. Don't compile with some gcc extensions enabled. – Lundin Dec 06 '17 at 15:28
  • Yep, just checked with GCC in standard mode. This code won't compile. Very smart trick, but not valid C, unfortunately. – Lundin Dec 06 '17 at 15:40
  • @Lundin: Oh. I didn't know that a variadic macro has to use `__VA_ARGS__` somewhere. I get a warning in standard mode. Strangely, I can make it go away by adding a dummy argument after `add2` in `add`, which is fishy, because it doesn't change the fact that I ignore the variadic arguments in `ADD`. It makes the variadic args a non-empty list, though. – M Oehm Dec 06 '17 at 19:56
  • I know that gcc has various non-standard extensions here, hence I compiled with `gcc -std=c11 -pedantic-errors` which doesn't let through non-compliant code. I'm not sure if making the `...` argument non-empty is enough for standard compliance. – Lundin Dec 07 '17 at 08:04
  • 1
    @Lundin: Hmmm. I get a different error message with a newer gcc. It seems that is is legal not to use the variadic args, but it isn't legal to pass zero variadic arguments. If that's true, adding the dummy argument works. I've tried with an admittedly dated version of Visual studio, and it doesn't compile at all. (That doesn't say anything about standard compliance, though.) – M Oehm Dec 07 '17 at 08:19
  • And the OP seems to have lost interest in the question. I don't think the `add` scenario is what they are after anyways -- the "variadic" way to add several numbers is to use the `+` operator several times. If the real intention is shown, a more C-ish solution without macros could be proposed. – M Oehm Dec 07 '17 at 08:20
4

That usual trick for counting arguments may be adapted for this:

#define ADD_EXPAND(...) \
        ADD_EXPAND_(__VA_ARGS__, EXPAND_ADD_FUNCS())

#define ADD_EXPAND_(...) \
        EXPAND_ADD_SEL_FUNC(__VA_ARGS__)

#define EXPAND_ADD_SEL_FUNC(first_, second_, third_, func, ...) func

#define EXPAND_ADD_FUNCS() foo2, foo, dummy

#define add(...) ADD_EXPAND(__VA_ARGS__)(__VA_ARGS__)

Once you plow through the boiler plate, it basically just involves placing all the arguments in a line, with the function tokens after them, and seeing which function stands out. That's what EXPAND_ADD_SEL_FUNC does.

You can see it live on coliru.

But I'll reiterate what we told you in comments. This is likely to be a sub-par solution to a proper function. I haven't tested it thoroughly, so breakage is easily possible. Use at your own risk.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
2

If you must use variadic macros, then here is a trick.

#define add(...) _Generic ( &(int[]){__VA_ARGS__}, \
                            int(*)[2]: add2,       \
                            int(*)[3]: add3) (__VA_ARGS__)
  • Have the macro create a compound literal array. The size of this array will depend on the number of arguments.
  • Grab the address of the compound literal, to get an array pointer type.
  • Let _Generic check which type you got, then call the proper function based on that.

This is 100% standard C and also type safe.


Demo:

#include <stdio.h>


#define add(...) _Generic ( &(int[]){__VA_ARGS__}, \
                            int(*)[2]: add2,       \
                            int(*)[3]: add3) (__VA_ARGS__)

int add2 (int a, int b);
int add3 (int a, int b, int c);

int main (void)
{
  printf("%d\n", add(1, 2));
  printf("%d\n", add(1, 2, 3));
//printf("%d\n", add(1, 2, 3, 4)); Compiler error for this.
}


int add2 (int a, int b)
{
  return a + b;
}

int add3 (int a, int b, int c)
{
  return a + b + c;
}
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • This solution has some minor drawbacks, namely that the function arguments must be assignment compatible to `int`, and even if they are, you might get weird warnings. For the latter, using `_Complex long double` instead of `int` would allow to capture all arithmetic types. – Jens Gustedt Dec 20 '17 at 12:43
  • @JensGustedt It is intentional, since variadic-macros have non-existent type safety. The code will only compile if compatible/implicitly convertible types are passed. Character types, floating point etc will work, but pointer types on aggregates etc will not be accepted. Getting diagnostics is a good thing, implicit and silent conversions and is a bad thing. – Lundin Dec 20 '17 at 15:34
  • 1
    This is 100% standard C11. I don't think this pass into a C90 nor C99 compiler. – Cyan Apr 08 '21 at 06:54
  • @Cyan There is only one C standard. That's kind of the whole point of standardization. That standard is currently ISO 9899:2018. When this was written, the C standard was ISO 9899:2011. ISO 9899:1999 was withdrawn over a decade ago. – Lundin Apr 08 '21 at 08:01
  • 1
    Semantic smokescreen. This will do little to help people who must write portable code, and have to pay attention if a given expression will work on C90 or C99 compilers. That your suggestion only works on more recent C11 compilers is a rather useful information that should haver been mentioned to the reader. In contrast, Oehm's answer just below is compatible with all compilers that support variadic macros, from C99 onwards (and virtually all C90 I'm aware of). A difference worth underlining. – Cyan Apr 08 '21 at 08:21
  • @Cyan According to the SO policies about C questions (and all other programming languages too), the current standard is assumed unless the person writing the question is saying otherwise. You can read about it here https://stackoverflow.com/tags/c/info. – Lundin Apr 08 '21 at 10:02
  • @Cyan Summary: "- If your question is specific to one particular version of the the language, add c90 c99 c11 or c17. - Unless the question explicitly mentions which version of the C standard that is used, it is assumed that the current version is used. That is, whichever version of ISO 9899 that ISO currently lists as active. Please have this in mind when answering or commenting on questions tagged c." – Lundin Apr 08 '21 at 10:02
  • Now if someone maintaining old C90 stuff stumbles upon this thread and decide to grab the code from here and import it in their old C90 code base, then they are thoroughly incompetent and should not be working with code maintenance. Maintaining such old code is much harder and more qualified work than writing new code - you shouldn't assign some rookie or quack to such tasks. For example variadic macros won't work in C90 either and you should know that if you work with C90 maintenance. – Lundin Apr 08 '21 at 10:08