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

3
  • 1
    Why can't add be recoded as a function that takes a variable argument list? Commented Dec 6, 2017 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. Commented Dec 6, 2017 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. Commented Dec 6, 2017 at 15:00

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

7 Comments

Yours is way more readable than my shot at it, I must say. Definite +1
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.
Yep, just checked with GCC in standard mode. This code won't compile. Very smart trick, but not valid C, unfortunately.
@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.
@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.)
|
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.

Comments

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;
}

8 Comments

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.
@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.
This is 100% standard C11. I don't think this pass into a C90 nor C99 compiler.
@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.
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.
|

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.