Nathaniel Flath
Nathaniel Flath

Reputation: 16025

C default arguments

Is there a way to specify default arguments to a function in C?

Upvotes: 376

Views: 325300

Answers (25)

Gunwoo Gim
Gunwoo Gim

Reputation: 403

I think this way of implementing default values for function arguments in C is one of the coolest ways in my honest opinion. I implemented it without using the compound struct literal hack so that the compiler utilizes registers like rdi,rsi,rdx,rcx for the arguments in amd64; I prefer the small number of CPU cycles saved to the nice benefits coming from the compound struct literal hack: named arguments, freely ordered arguments and the ability to omit any arguments for value 0:

/**
 * \brief Verbatim! You can use it to pass more than one argument packed as
 * one single argument into a macro function.
 */
#define V(...) __VA_ARGS__

#define __PICK_64TH( \
  _1, _2, _3, _4, _5, _6, _7, _8, _9, _10,  \
  _11,_12,_13,_14,_15,_16,_17,_18,_19,_20,  \
  _21,_22,_23,_24,_25,_26,_27,_28,_29,_30,  \
  _31,_32,_33,_34,_35,_36,_37,_38,_39,_40,  \
  _41,_42,_43,_44,_45,_46,_47,_48,_49,_50,  \
  _51,_52,_53,_54,_55,_56,_57,_58,_59,_60,  \
  _61,_62,_63, N, ...) N

#define __SEQ_FROM_63_TO_0() \
  63, 62, 61, 60, \
  59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \
  49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \
  39, 38, 37, 36, 35, 34, 33, 32, 31, 30, \
  29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \
  19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \
   9,  8,  7,  6,  5,  4,  3,  2,  1,  0

#define __ARG_CNT_IMPL(...) __PICK_64TH(__VA_ARGS__)

#define __SEQ_FOR_COMMA_DETECTION() \
  1,  1,  1,  1,                          \
  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  \
  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  \
  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  \
  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  \
  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  \
  1,  1,  1,  1,  1,  1,  1,  1,  0,  0

#define __HASCOMMA(...) \
  __ARG_CNT_IMPL(__VA_ARGS__, __SEQ_FOR_COMMA_DETECTION())

#define __COMMA(...)  ,

#define __NARG_HELPER1(a, b, N)  __NARG_HELPER2(a, b, N)
#define __NARG_HELPER2(a, b, N)  __NARG_HELPER3_ ## a ## b(N)
#define __NARG_HELPER3_01(N)     1
#define __NARG_HELPER3_00(N)     0
#define __NARG_HELPER3_11(N)     N

#define __NARG_IMPL(...) \
  __NARG_HELPER1( \
      __HASCOMMA(__VA_ARGS__), \
      __HASCOMMA(__COMMA __VA_ARGS__), \
      __ARG_CNT_IMPL(__VA_ARGS__, __SEQ_FROM_63_TO_0()))

#define __start_glueing(a, b) __glue_impl(a, b)
#define __glue_impl(a, b) a ## b

#define __VA_ARGCNT_WITH_UNDERSCORE(...) \
  __start_glueing(_, __NARG_IMPL(__VA_ARGS__))

#define __vafunc_impl(func_name, ...) \
  __start_glueing(func_name, __VA_ARGCNT_WITH_UNDERSCORE(__VA_ARGS__))
/**
 * \details Usage Example:
 *   #define foo(...) _vafunc(foo, __VA_ARGS__)
 *   foo(0, 0)  // gets converted to foo2(0, 0)
 */
#define _vafunc(func_name, ...) \
  __vafunc_impl(func_name, __VA_ARGS__) (__VA_ARGS__)



#define __REPLACE_0_WITH_DEF_VAL_1(def1, arg1) \
      (arg1) ? (arg1) : (def1)
#define __REPLACE_0_WITH_DEF_VAL_2(def1, def2, arg1, arg2) \
      (arg1) ? (arg1) : (def1), \
      (arg2) ? (arg2) : (def2)
#define __REPLACE_0_WITH_DEF_VAL_3(def1, def2, def3, arg1, arg2, arg3) \
      (arg1) ? (arg1) : (def1), \
      (arg2) ? (arg2) : (def2), \
      (arg3) ? (arg3) : (def3)
#define __REPLACE_0_WITH_DEF_VAL_4(def1, def2, def3, def4, arg1, arg2, arg3, arg4) \
      (arg1) ? (arg1) : (def1), \
      (arg2) ? (arg2) : (def2), \
      (arg3) ? (arg3) : (def3), \
      (arg4) ? (arg4) : (def4)
#define __REPLACE_0_WITH_DEF_VAL_5(def1, def2, def3, def4, def5, arg1, arg2, arg3, arg4, arg5) \
      (arg1) ? (arg1) : (def1), \
      (arg2) ? (arg2) : (def2), \
      (arg3) ? (arg3) : (def3), \
      (arg4) ? (arg4) : (def4), \
      (arg5) ? (arg5) : (def5)
#define __REPLACE_0_WITH_DEF_VAL_6(def1, def2, def3, def4, def5, def6, arg1, arg2, arg3, arg4, arg5, arg6) \
      (arg1) ? (arg1) : (def1), \
      (arg2) ? (arg2) : (def2), \
      (arg3) ? (arg3) : (def3), \
      (arg4) ? (arg4) : (def4), \
      (arg5) ? (arg5) : (def5), \
      (arg6) ? (arg6) : (def6)
#define __REPLACE_0_WITH_DEF_VAL_7(def1, def2, def3, def4, def5, def6, def7, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \
      (arg1) ? (arg1) : (def1), \
      (arg2) ? (arg2) : (def2), \
      (arg3) ? (arg3) : (def3), \
      (arg4) ? (arg4) : (def4), \
      (arg5) ? (arg5) : (def5), \
      (arg6) ? (arg6) : (def6), \
      (arg7) ? (arg7) : (def7)
#define __REPLACE_0_WITH_DEF_VAL_8(def1, def2, def3, def4, def5, def6, def7, def8, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \
      (arg1) ? (arg1) : (def1), \
      (arg2) ? (arg2) : (def2), \
      (arg3) ? (arg3) : (def3), \
      (arg4) ? (arg4) : (def4), \
      (arg5) ? (arg5) : (def5), \
      (arg6) ? (arg6) : (def6), \
      (arg7) ? (arg7) : (def7), \
      (arg8) ? (arg8) : (def8)

// Exuse me for the wall of text here!

#define __REPLACE_0_WITH_DEF_VAL_9(def1, def2, def3, def4, def5, def6, def7, def8, def9, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) \
      (arg1) ? (arg1) : (def1), \
      (arg2) ? (arg2) : (def2), \
      (arg3) ? (arg3) : (def3), \
      (arg4) ? (arg4) : (def4), \
      (arg5) ? (arg5) : (def5), \
      (arg6) ? (arg6) : (def6), \
      (arg7) ? (arg7) : (def7), \
      (arg8) ? (arg8) : (def8), \
      (arg9) ? (arg9) : (def9)
#define __REPLACE_0_WITH_DEF_VAL_10(def1, def2, def3, def4, def5, def6, def7, def8, def9, def10, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) \
      (arg1) ? (arg1) : (def1), \
      (arg2) ? (arg2) : (def2), \
      (arg3) ? (arg3) : (def3), \
      (arg4) ? (arg4) : (def4), \
      (arg5) ? (arg5) : (def5), \
      (arg6) ? (arg6) : (def6), \
      (arg7) ? (arg7) : (def7), \
      (arg8) ? (arg8) : (def8), \
      (arg9) ? (arg9) : (def9), \
      (arg10) ? (arg10) : (def10)
#define __REPLACE_0_WITH_DEF_VAL_11(def1, def2, def3, def4, def5, def6, def7, def8, def9, def10, def11, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11) \
      (arg1) ? (arg1) : (def1), \
      (arg2) ? (arg2) : (def2), \
      (arg3) ? (arg3) : (def3), \
      (arg4) ? (arg4) : (def4), \
      (arg5) ? (arg5) : (def5), \
      (arg6) ? (arg6) : (def6), \
      (arg7) ? (arg7) : (def7), \
      (arg8) ? (arg8) : (def8), \
      (arg9) ? (arg9) : (def9), \
      (arg10) ? (arg10) : (def10), \
      (arg11) ? (arg11) : (def11)
#define __REPLACE_0_WITH_DEF_VAL_12(def1, def2, def3, def4, def5, def6, def7, def8, def9, def10, def11, def12, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12) \
      (arg1) ? (arg1) : (def1), \
      (arg2) ? (arg2) : (def2), \
      (arg3) ? (arg3) : (def3), \
      (arg4) ? (arg4) : (def4), \
      (arg5) ? (arg5) : (def5), \
      (arg6) ? (arg6) : (def6), \
      (arg7) ? (arg7) : (def7), \
      (arg8) ? (arg8) : (def8), \
      (arg9) ? (arg9) : (def9), \
      (arg10) ? (arg10) : (def10), \
      (arg11) ? (arg11) : (def11), \
      (arg12) ? (arg12) : (def12)
#define __REPLACE_0_WITH_DEF_VAL_13(def1, def2, def3, def4, def5, def6, def7, def8, def9, def10, def11, def12, def13, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13) \
      (arg1) ? (arg1) : (def1), \
      (arg2) ? (arg2) : (def2), \
      (arg3) ? (arg3) : (def3), \
      (arg4) ? (arg4) : (def4), \
      (arg5) ? (arg5) : (def5), \
      (arg6) ? (arg6) : (def6), \
      (arg7) ? (arg7) : (def7), \
      (arg8) ? (arg8) : (def8), \
      (arg9) ? (arg9) : (def9), \
      (arg10) ? (arg10) : (def10), \
      (arg11) ? (arg11) : (def11), \
      (arg12) ? (arg12) : (def12), \
      (arg13) ? (arg13) : (def13)
#define __REPLACE_0_WITH_DEF_VAL_14(def1, def2, def3, def4, def5, def6, def7, def8, def9, def10, def11, def12, def13, def14, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14) \
      (arg1) ? (arg1) : (def1), \
      (arg2) ? (arg2) : (def2), \
      (arg3) ? (arg3) : (def3), \
      (arg4) ? (arg4) : (def4), \
      (arg5) ? (arg5) : (def5), \
      (arg6) ? (arg6) : (def6), \
      (arg7) ? (arg7) : (def7), \
      (arg8) ? (arg8) : (def8), \
      (arg9) ? (arg9) : (def9), \
      (arg10) ? (arg10) : (def10), \
      (arg11) ? (arg11) : (def11), \
      (arg12) ? (arg12) : (def12), \
      (arg13) ? (arg13) : (def13), \
      (arg14) ? (arg14) : (def14)
#define __REPLACE_0_WITH_DEF_VAL_15(def1, def2, def3, def4, def5, def6, def7, def8, def9, def10, def11, def12, def13, def14, def15, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15) \
      (arg1) ? (arg1) : (def1), \
      (arg2) ? (arg2) : (def2), \
      (arg3) ? (arg3) : (def3), \
      (arg4) ? (arg4) : (def4), \
      (arg5) ? (arg5) : (def5), \
      (arg6) ? (arg6) : (def6), \
      (arg7) ? (arg7) : (def7), \
      (arg8) ? (arg8) : (def8), \
      (arg9) ? (arg9) : (def9), \
      (arg10) ? (arg10) : (def10), \
      (arg11) ? (arg11) : (def11), \
      (arg12) ? (arg12) : (def12), \
      (arg13) ? (arg13) : (def13), \
      (arg14) ? (arg14) : (def14), \
      (arg15) ? (arg15) : (def15)
#define __REPLACE_0_WITH_DEF_VAL_16(def1, def2, def3, def4, def5, def6, def7, def8, def9, def10, def11, def12, def13, def14, def15, def16, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16) \
      (arg1) ? (arg1) : (def1), \
      (arg2) ? (arg2) : (def2), \
      (arg3) ? (arg3) : (def3), \
      (arg4) ? (arg4) : (def4), \
      (arg5) ? (arg5) : (def5), \
      (arg6) ? (arg6) : (def6), \
      (arg7) ? (arg7) : (def7), \
      (arg8) ? (arg8) : (def8), \
      (arg9) ? (arg9) : (def9), \
      (arg10) ? (arg10) : (def10), \
      (arg11) ? (arg11) : (def11), \
      (arg12) ? (arg12) : (def12), \
      (arg13) ? (arg13) : (def13), \
      (arg14) ? (arg14) : (def14), \
      (arg15) ? (arg15) : (def15), \
      (arg16) ? (arg16) : (def16)


#define __ARGS_FOR_CALL_FUNC_WITH_DEF_VAL_IMPL(...) \
  __start_glueing( __REPLACE_0_WITH_DEF_VAL_, __NARG_IMPL(__VA_ARGS__) )

#define __ARGS_FOR_CALL_FUNC_WITH_DEF_VAL(defvals, ...) \
  __ARGS_FOR_CALL_FUNC_WITH_DEF_VAL_IMPL(V(defvals)) (defvals, __VA_ARGS__)

/**
 * \brief Call a function with default values, a default value replaces a 0.
 * \details
 *   Usage Example:
 *   int add_(int a, int b) {
 *     return a + b;
 *   }
 *   #define add(...) _cwdv(add_, V(0, 100), __VA_ARGS__)
 *   fprintf(stdout, "%d\n", add(8,0));  // 108
 */
#define _cwdv(func_name, defvals, ...) \
  func_name( \
      __ARGS_FOR_CALL_FUNC_WITH_DEF_VAL(V(defvals), __VA_ARGS__) \
      )

Please take a look at the comments for usage example :) _cwdv is the macro you were looking for.

As many programmers would agree, I would love to see the ISO group for C programming language just bring the feature into C2Y; Not many people are willing to learn how to use regular expressions and a sophisticated text editor like vim to produce this kind of macro!

And you can also combine it with the macro called _vafunc in the code above if you'd like the user to be able to omit writing 0 for a specific argument or two:

int add_2_(int a, int b) { return a+b; }  // Or return a+b+0+100
int add_3_(int a, int b, int c) { return a+b+c+100; }
int add_4_(int a, int b, int c, int d) { return a+b+c+d; }
#define add_2(...) _cwdv(add_2_, V(0, 100), __VA_ARGS__)
#define add_3(...) _cwdv(add_3_, V(0, 100, 0), __VA_ARGS__)
#define add_4(...) _cwdv(add_4_, V(0, 100, 0, 100), __VA_ARGS__)
#define add(...) _vafunc(add, __VA_ARGS__)

You can even have function overloading with default values via C11 _Generic:

int add_2_(int a, int b) { return a+b; }  // Or return a+b+0+100
#define add_2(...) _cwdv(add_2_, V(0, 100), __VA_ARGS__)

int add_3_c(int a, int b, char c) { return a+b+c+100; }
#define add_3_c__(...) _cwdv(add_3_c, V(0, 100, 0), __VA_ARGS__)
int add_3_i(int a, int b, int c) { return a+b+c+500; }
#define add_3_i__(...) _cwdv(add_3_i, V(0, 100, 0), __VA_ARGS__)
__attribute__((always_inline)) inline int add_3_c_(int a, int b, char c) { return add_3_c__(a, b, c); }
__attribute__((always_inline)) inline int add_3_i_(int a, int b, int c) { return add_3_i__(a, b, c); }
#define add_3(a1, a2, a3) _Generic((a3), \
    unsigned char : add_3_c_, \
    char  : add_3_c_, \
    int   : add_3_i_ \
    )(a1, a2, a3)

int add_4_(int a, int b, int c, int d) { return a+b+c+d; }
#define add_4(...) _cwdv(add_4_, V(0, 100, 0, 100), __VA_ARGS__)

#define add(...) _vafunc(add, __VA_ARGS__)


int main(int argc, char * argv[]) {
  fprintf(stdout, "%d\n", add(8, 0));  // 108
  fprintf(stdout, "%d\n", add(8, 0, 0));  // 608
  fprintf(stdout, "%d\n", add(8, 0, (char)10));  // 218
  fprintf(stdout, "%d\n", add(8, 0, 0, 0));  // 208
  return 0;
}

Upvotes: 0

Hong승원
Hong승원

Reputation: 146

I made it appropriately by bk. and Eric.

#include <stdio.h>

// 구조체 생성
typedef struct range {
  int a;
} f_args;

// 매개변수 기본값 설정
#define func(...) func((f_args){.a = 20, __VA_ARGS__})
f_args(func)(f_args x);

int main(void) {
  // 매개변수 기본값 설정(선택 1)
  f_args r = func();

  // 매개변수 값 변환(선택 2)
  // f_args r = func(.a = 10);

  printf("%d ", r.a); //확인

  return 0;
}

// 매개변수 기본값 구성 가능한 함수
f_args(func)(f_args x) {
  printf("%d ", x.a); //확인
  x.a = x.a + 10;

  return x;
}

The default value must be changed in #define (variadic macros)

Upvotes: -1

Wim ten Brink
Wim ten Brink

Reputation: 26682

Yes. :-) But not in a way you would expect.

int f1(int arg1, double arg2, char* name, char *opt);

int f2(int arg1, double arg2, char* name)
{
  return f1(arg1, arg2, name, "Some option");
}

Unfortunately, C doesn't allow you to overload methods so you'd end up with two different functions. Still, by calling f2, you'd actually be calling f1 with a default value. This is a "Don't Repeat Yourself" solution, which helps you to avoid copying/pasting existing code.

Upvotes: 191

Lundin
Lundin

Reputation: 214770

There's a trick I've occasionally used, which has been available since C99, using variadic macros, compound literals and designated initializers. As with any macro solution, it is cumbersome and generally not recommended other than as a last resort...

My method is built in the following way:

  • Wrap the actual function in a function-like, variadic macro:

    void myfunc (int x, int y)         // actual function
    #define myfunc(...) myfunc(params) // wrapper macro
    
  • By using compound literals, copy down the parameters passed into a temporary object. This object should be a private struct corresponding directly to the function's expected parameter list. Example:

    typedef struct
    {
      int x;
      int y;
    } myfunc_t;
    
    #define PASSED_ARGS(...) (myfunc_t){__VA_ARGS__}
    

    This means that the same type safety ("as per assignment") rules used when passing parameters to a function is also used when initializing this struct. We don't lose any type safety. Similarly, this automatically guards against providing too many arguments.

  • However, the above doesn't cover the case of an empty argument list. To counter this, add a dummy argument so that the initializer list is never empty:

    typedef struct
    {
      int dummy;
      int x;
      int y;
    } myfunc_t;
    
    #define PASSED_ARGS(...) (myfunc_t){0,__VA_ARGS__}
    
  • Similarly, we can count the number of arguments passed, assuming that every parameter passed can get implicitly converted to int:

    #define COUNT_ARGS(...) (sizeof(int[]){0,__VA_ARGS__} / sizeof(int) - 1)

  • We define a macro for the default arguments #define DEFAULT_ARGS (myfunc_t){0,1,2}, where 0 is the dummy and 1,2 are the default ones.

  • Wrapping all of this together, the outermost wrapper macro may look like:

    #define myfunc(...) myfunc( MYFUNC_INIT(__VA_ARGS__).x, MYFUNC_INIT(__VA_ARGS__).y )

    This assuming that the inner macro MYFUNC_INIT returns a myfunc_t struct.

  • The inner macro conditionally picks struct initializers based on the size of the argument list. In case the argument list is short, it fills up with default arguments.

    #define MYFUNC_INIT(...) \
      (myfunc_t){ 0,         \
                  .x = COUNT_ARGS(__VA_ARGS__)==0 ? DEFAULT_ARGS.x : PASSED_ARGS(__VA_ARGS__).x, \
                  .y = COUNT_ARGS(__VA_ARGS__)<2  ? DEFAULT_ARGS.y : PASSED_ARGS(__VA_ARGS__).y, \
                }
    

Full example:

#include <stdio.h>

void myfunc (int x, int y)
{
  printf("x:%d y:%d\n", x, y);
}

typedef struct
{
  int dummy;
  int x;
  int y;
} myfunc_t;

#define DEFAULT_ARGS (myfunc_t){0,1,2}
#define PASSED_ARGS(...) (myfunc_t){0,__VA_ARGS__}
#define COUNT_ARGS(...) (sizeof(int[]){0,__VA_ARGS__} / sizeof(int) - 1)
#define MYFUNC_INIT(...) \
  (myfunc_t){ 0,         \
              .x = COUNT_ARGS(__VA_ARGS__)==0 ? DEFAULT_ARGS.x : PASSED_ARGS(__VA_ARGS__).x, \
              .y = COUNT_ARGS(__VA_ARGS__)<2  ? DEFAULT_ARGS.y : PASSED_ARGS(__VA_ARGS__).y, \
            }

#define myfunc(...) myfunc( MYFUNC_INIT(__VA_ARGS__).x, MYFUNC_INIT(__VA_ARGS__).y )

int main (void)
{
  myfunc(3,4);
  myfunc(3);
  myfunc();
}

Output:

x:3 y:4
x:3 y:2
x:1 y:2

Godbolt: https://godbolt.org/z/4ns1zPW16 As you can see from the -O3 disassembly, there is zero overhead from the compound literals.


I noticed that my method reminds a bit of the current, top-voted answer. For comparison with other solutions here:

Pros:

  • Pure, portable standard ISO C, no dirty gcc extensions, no poorly-defined behavior.
  • Can handle empty argument lists.
  • Efficient, zero overhead, doesn't rely on function inlining getting carried out as expected.
  • No obscure designated initializers on the caller-side.

Cons:

  • Relies on every parameter being implicitly convertible to int, which often isn't the case. For example strict C does not allow implicit conversions from pointers to int - such implicit conversions is a non-conforming (but popular) compiler extension.
  • Default args and structs have to be generated per function. Although not covered by this answer, this could be automated with X macros. But doing so will also reduce readability even further.

Upvotes: 2

Pooia
Pooia

Reputation: 51

I know how to do this in a better manner. You simply assign NULL to a parameter, so, you will have no value. Then you check if the parameter value is NULL, you change it to the default value.

void func(int x){
if(x == NULL)
  x = 2;
....
}

Though, it will cause warnings. a better choice would be to assign a value that will do nothing if the parameter value is that:

void func(int x){
if(x == 1)
  x = 2;
....
}

In the example above, if x is 1 the function changes it to 2;

Thanks to @user904963, EDIT: if you have to cover all ranges of numbers, it's not hard to add another argument only to say to the function whether it would set the parameter to default or not

void func(int x, bool useDefault){
if(useDefault) //useDefault == true
  x = 2;
....
}

However, remember to include stdbool.h

Upvotes: 0

Igor Gomes
Igor Gomes

Reputation: 134

https://github.com/cindRoberta/C/blob/master/structure/function/default_parameter.c

#include<stdio.h>

void f_impl(int a, float b) {
  printf("%d %g\n", a, b);
}

#define f_impl(...) f_macro(__VA_ARGS__, 3.7)
#define f_macro(a, b, ...) f_impl(a, b)

int main(void) {
  f_impl(1);
  f_impl(1, 2, 3, 4);

  return 0;
}

Upvotes: 0

user10658782
user10658782

Reputation: 51

you don't need to use VARARGS with just C. Here is an example.

int funcA_12(int a1, int a2) { ... }

#define funcA(a1) funcA_12(a1, 0)

This answer is very similar to the two functions method above but in this case, you're using a macro for the function name that defines the arguments.

Upvotes: 0

David Ranieri
David Ranieri

Reputation: 41046

Another trick using macros:

#include <stdio.h>

#define func(...) FUNC(__VA_ARGS__, 15, 0)
#define FUNC(a, b, ...) func(a, b)

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

int main(void)
{
    printf("%d\n", func(1));
    printf("%d\n", func(1, 2));
    return 0;
}

If only one argument is passed, b receives the default value (in this case 15)

Upvotes: 16

Ramu
Ramu

Reputation: 127

YES

Through macros

3 Parameters:

#define my_func2(...) my_func3(__VA_ARGS__, 0.5)
#define my_func1(...) my_func2(__VA_ARGS__, 10)
#define VAR_FUNC(_1, _2, _3, NAME, ...) NAME
#define my_func(...) VAR_FUNC(__VA_ARGS__, my_func3, my_func2, my_func1)(__VA_ARGS__)

void my_func3(char a, int b, float c) // b=10, c=0.5
{
    printf("a=%c; b=%d; c=%f\n", a, b, c);
}

If you want 4th argument, then an extra my_func3 needs to be added. Notice the changes in VAR_FUNC, my_func2 and my_func

4 Parameters:

#define my_func3(...) my_func4(__VA_ARGS__, "default") // <== New function added
#define my_func2(...) my_func3(__VA_ARGS__, (float)1/2)
#define my_func1(...) my_func2(__VA_ARGS__, 10)
#define VAR_FUNC(_1, _2, _3, _4, NAME, ...) NAME
#define my_func(...) VAR_FUNC(__VA_ARGS__, my_func4, my_func3, my_func2, my_func1)(__VA_ARGS__)

void my_func4(char a, int b, float c, const char* d) // b=10, c=0.5, d="default"
{
    printf("a=%c; b=%d; c=%f; d=%s\n", a, b, c, d);
}

Only exception that float variables cannot be given default values (unless if it is the last argument as in the 3 parameters case), because they need period ('.'), which is not accepted within macro arguments. But can figure out a work around as seen in my_func2 macro (of 4 parameters case)

Program

int main(void)
{
    my_func('a');
    my_func('b', 20);
    my_func('c', 200, 10.5);
    my_func('d', 2000, 100.5, "hello");

    return 0;
}

Output:

a=a; b=10; c=0.500000; d=default                                                                                                                                                  
a=b; b=20; c=0.500000; d=default                                                                                                                                                  
a=c; b=200; c=10.500000; d=default                                                                                                                                                
a=d; b=2000; c=100.500000; d=hello  

Upvotes: 2

Shelby Moore III
Shelby Moore III

Reputation: 6131

I improved Jens Gustedt’s answer so that:

  1. inline functions aren’t employed
  2. defaults are computed during preprocessing
  3. modular reuseable macros
  4. possible to set compiler error that meaningfully matches the case of insufficient arguments for the allowed defaults
  5. the defaults aren’t required to form the tail of the parameter list if the argument types will remain unambiguous
  6. interopts with C11 _Generic
  7. vary the function name by the number of arguments!

variadic.h:

#ifndef VARIADIC

#define _NARG2(_0, _1, _2, ...) _2
#define NUMARG2(...) _NARG2(__VA_ARGS__, 2, 1, 0)
#define _NARG3(_0, _1, _2, _3, ...) _3
#define NUMARG3(...) _NARG3(__VA_ARGS__, 3, 2, 1, 0)
#define _NARG4(_0, _1, _2, _3, _4, ...) _4
#define NUMARG4(...) _NARG4(__VA_ARGS__, 4, 3, 2, 1, 0)
#define _NARG5(_0, _1, _2, _3, _4, _5, ...) _5
#define NUMARG5(...) _NARG5(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
#define _NARG6(_0, _1, _2, _3, _4, _5, _6, ...) _6
#define NUMARG6(...) _NARG6(__VA_ARGS__, 6, 5, 4, 3, 2, 1, 0)
#define _NARG7(_0, _1, _2, _3, _4, _5, _6, _7, ...) _7
#define NUMARG7(...) _NARG7(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
#define _NARG8(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) _8
#define NUMARG8(...) _NARG8(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define _NARG9(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) _9
#define NUMARG9(...) _NARG9(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define __VARIADIC(name, num_args, ...) name ## _ ## num_args (__VA_ARGS__)
#define _VARIADIC(name, num_args, ...) name (__VARIADIC(name, num_args, __VA_ARGS__))
#define VARIADIC(name, num_args, ...) _VARIADIC(name, num_args, __VA_ARGS__)
#define VARIADIC2(name, num_args, ...) __VARIADIC(name, num_args, __VA_ARGS__)

// Vary function name by number of arguments supplied
#define VARIADIC_NAME(name, num_args) name ## _ ## num_args ## _name ()
#define NVARIADIC(name, num_args, ...) _VARIADIC(VARIADIC_NAME(name, num_args), num_args, __VA_ARGS__)

#endif

Simplified usage scenario:

const uint32*
uint32_frombytes(uint32* out, const uint8* in, size_t bytes);

/*
The output buffer defaults to NULL if not provided.
*/

#include "variadic.h"

#define uint32_frombytes_2(   b, c) NULL, b, c
#define uint32_frombytes_3(a, b, c)    a, b, c
#define uint32_frombytes(...) VARIADIC(uint32_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

And with _Generic:

const uint8*
uint16_tobytes(const uint16* in, uint8* out, size_t bytes);

const uint16*
uint16_frombytes(uint16* out, const uint8* in, size_t bytes);

const uint8*
uint32_tobytes(const uint32* in, uint8* out, size_t bytes);

const uint32*
uint32_frombytes(uint32* out, const uint8* in, size_t bytes);

/*
The output buffer defaults to NULL if not provided.
Generic function name supported on the non-uint8 type, except where said type
is unavailable because the argument for output buffer was not provided.
*/

#include "variadic.h"

#define   uint16_tobytes_2(a,    c) a, NULL, c
#define   uint16_tobytes_3(a, b, c) a,    b, c
#define   uint16_tobytes(...) VARIADIC(  uint16_tobytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define uint16_frombytes_2(   b, c) NULL, b, c
#define uint16_frombytes_3(a, b, c)    a, b, c
#define uint16_frombytes(...) VARIADIC(uint16_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define   uint32_tobytes_2(a,    c) a, NULL, c
#define   uint32_tobytes_3(a, b, c) a,    b, c
#define   uint32_tobytes(...) VARIADIC(  uint32_tobytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define uint32_frombytes_2(   b, c) NULL, b, c
#define uint32_frombytes_3(a, b, c)    a, b, c
#define uint32_frombytes(...) VARIADIC(uint32_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define   tobytes(a, ...) _Generic((a),                                                                                                 \
                                   const uint16*: uint16_tobytes,                                                                       \
                                   const uint32*: uint32_tobytes)  (VARIADIC2(  uint32_tobytes, NUMARG3(a, __VA_ARGS__), a, __VA_ARGS__))

#define frombytes(a, ...) _Generic((a),                                                                                                 \
                                         uint16*: uint16_frombytes,                                                                     \
                                         uint32*: uint32_frombytes)(VARIADIC2(uint32_frombytes, NUMARG3(a, __VA_ARGS__), a, __VA_ARGS__))

And with variadic function name selection, which can't be combined with _Generic:

// winternitz() with 5 arguments is replaced with merkle_lamport() on those 5 arguments.

#define   merkle_lamport_5(a, b, c, d, e) a, b, c, d, e
#define   winternitz_7(a, b, c, d, e, f, g) a, b, c, d, e, f, g
#define   winternitz_5_name() merkle_lamport
#define   winternitz_7_name() winternitz
#define   winternitz(...) NVARIADIC(winternitz, NUMARG7(__VA_ARGS__), __VA_ARGS__)

Upvotes: 4

user1129665
user1129665

Reputation:

OpenCV uses something like:

/* in the header file */

#ifdef __cplusplus
    /* in case the compiler is a C++ compiler */
    #define DEFAULT_VALUE(value) = value
#else
    /* otherwise, C compiler, do nothing */
    #define DEFAULT_VALUE(value)
#endif

void window_set_size(unsigned int width  DEFAULT_VALUE(640),
                     unsigned int height DEFAULT_VALUE(400));

If the user doesn't know what he should write, this trick can be helpful:

usage example

Upvotes: 24

navgupta
navgupta

Reputation: 1

Why can't we do this.

Give the optional argument a default value. In that way, the caller of the function don't necessarily need to pass the value of the argument. The argument takes the default value. And easily that argument becomes optional for the client.

For e.g.

void foo(int a, int b = 0);

Here b is an optional argument.

Upvotes: -6

Eli Courtwright
Eli Courtwright

Reputation: 193161

Not really. The only way would be to write a varargs function and manually fill in default values for arguments which the caller doesn't pass.

Upvotes: 173

u0b34a0f6ae
u0b34a0f6ae

Reputation: 49833

We can create functions which use named parameters (only) for default values. This is a continuation of bk.'s answer.

#include <stdio.h>                                                               

struct range { int from; int to; int step; };
#define range(...) range((struct range){.from=1,.to=10,.step=1, __VA_ARGS__})   

/* use parentheses to avoid macro subst */             
void (range)(struct range r) {                                                     
    for (int i = r.from; i <= r.to; i += r.step)                                 
        printf("%d ", i);                                                        
    puts("");                                                                    
}                                                                                

int main() {                                                                     
    range();                                                                    
    range(.from=2, .to=4);                                                      
    range(.step=2);                                                             
}    

The C99 standard defines that later names in the initialization override previous items. We can also have some standard positional parameters as well, just change the macro and function signature accordingly. The default value parameters can only be used in named parameter style.

Program output:

1 2 3 4 5 6 7 8 9 10 
2 3 4 
1 3 5 7 9

Upvotes: 53

Chris Lutz
Chris Lutz

Reputation: 75439

Yet another option uses structs:

struct func_opts {
  int    arg1;
  char * arg2;
  int    arg3;
};

void func(int arg, struct func_opts *opts)
{
    int arg1 = 0, arg3 = 0;
    char *arg2 = "Default";
    if(opts)
      {
        if(opts->arg1)
            arg1 = opts->arg1;
        if(opts->arg2)
            arg2 = opts->arg2;
        if(opts->arg3)
            arg3 = opts->arg3;
      }
    // do stuff
}

// call with defaults
func(3, NULL);

// also call with defaults
struct func_opts opts = {0};
func(3, &opts);

// set some arguments
opts.arg3 = 3;
opts.arg2 = "Yes";
func(3, &opts);

Upvotes: 15

Jens Gustedt
Jens Gustedt

Reputation: 78963

Yes, with features of C99 you may do this. This works without defining new data structures or so and without the function having to decide at runtime how it was called, and without any computational overhead.

For a detailed explanation see my post at

http://gustedt.wordpress.com/2010/06/03/default-arguments-for-c99/

Jens

Upvotes: 5

bk.
bk.

Reputation: 6288

Wow, everybody is such a pessimist around here. The answer is yes.

It ain't trivial: by the end, we'll have the core function, a supporting struct, a wrapper function, and a macro around the wrapper function. In my work I have a set of macros to automate all this; once you understand the flow it'll be easy for you to do the same.

I've written this up elsewhere, so here's a detailed external link to supplement the summary here: http://modelingwithdata.org/arch/00000022.htm

We'd like to turn

double f(int i, double x)

into a function that takes defaults (i=8, x=3.14). Define a companion struct:

typedef struct {
    int i;
    double x;
} f_args;

Rename your function f_base, and define a wrapper function that sets defaults and calls the base:

double var_f(f_args in){
    int i_out = in.i ? in.i : 8;
    double x_out = in.x ? in.x : 3.14;
    return f_base(i_out, x_out);
}

Now add a macro, using C's variadic macros. This way users don't have to know they're actually populating a f_args struct and think they're doing the usual:

#define f(...) var_f((f_args){__VA_ARGS__});

OK, now all of the following would work:

f(3, 8);      //i=3, x=8
f(.i=1, 2.3); //i=1, x=2.3
f(2);         //i=2, x=3.14
f(.x=9.2);    //i=8, x=9.2

Check the rules on how compound initializers set defaults for the exact rules.

One thing that won't work: f(0), because we can't distinguish between a missing value and zero. In my experience, this is something to watch out for, but can be taken care of as the need arises---half the time your default really is zero.

I went through the trouble of writing this up because I think named arguments and defaults really do make coding in C easier and even more fun. And C is awesome for being so simple and still having enough there to make all this possible.

Upvotes: 376

eaanon01
eaanon01

Reputation: 1079

Yes you can do somthing simulair, here you have to know the different argument lists you can get but you have the same function to handle then all.

typedef enum { my_input_set1 = 0, my_input_set2, my_input_set3} INPUT_SET;

typedef struct{
    INPUT_SET type;
    char* text;
} input_set1;

typedef struct{
    INPUT_SET type;
    char* text;
    int var;
} input_set2;

typedef struct{
    INPUT_SET type;
    int text;
} input_set3;

typedef union
{
    INPUT_SET type;
    input_set1 set1;
    input_set2 set2;
    input_set3 set3;
} MY_INPUT;

void my_func(MY_INPUT input)
{
    switch(input.type)
    {
        case my_input_set1:
        break;
        case my_input_set2:
        break;
        case my_input_set3:
        break;
        default:
        // unknown input
        break;
    }
}

Upvotes: 0

Short answer: No.

Slightly longer answer: There is an old, old workaround where you pass a string that you parse for optional arguments:

int f(int arg1, double arg2, char* name, char *opt);

where opt may include "name=value" pair or something, and which you would call like

n = f(2,3.0,"foo","plot=yes save=no");

Obviously this is only occasionally useful. Generally when you want a single interface to a family of functionality.


You still find this approach in particle physics codes that are written by professional programs in c++ (like for instance ROOT). It's main advantage is that it may be extended almost indefinitely while maintaining back compatibility.

Upvotes: 16

Michael Burr
Michael Burr

Reputation: 340406

Probably the best way to do this (which may or may not be possible in your case depending on your situation) is to move to C++ and use it as 'a better C'. You can use C++ without using classes, templates, operator overloading or other advanced features.

This will give you a variant of C with function overloading and default parameters (and whatever other features you chose to use). You just have to be a little disciplined if you're really serious about using only a restricted subset of C++.

A lot of people will say it's a terrible idea to use C++ in this way, and they might have a point. But's it's just an opinion; I think it's valid to use features of C++ that you're comfortable with without having to buy into the whole thing. I think a significant part of the reason for the sucess of C++ is that it got used by an awful lot of programmers in it's early days in exactly this way.

Upvotes: 17

sambowry
sambowry

Reputation: 2476

Generally no, but in gcc You may make the last parameter of funcA() optional with a macro.

In funcB() i use a special value (-1) to signal that i need the default value for the 'b' parameter.

#include <stdio.h> 

int funcA( int a, int b, ... ){ return a+b; }
#define funcA( a, ... ) funcA( a, ##__VA_ARGS__, 8 ) 


int funcB( int a, int b ){
  if( b == -1 ) b = 8;
  return a+b;
}

int main(void){
  printf("funcA(1,2): %i\n", funcA(1,2) );
  printf("funcA(1):   %i\n", funcA(1)   );

  printf("funcB(1, 2): %i\n", funcB(1, 2) );
  printf("funcB(1,-1): %i\n", funcB(1,-1) );
}

Upvotes: 3

David R Tribble
David R Tribble

Reputation: 12214

No, but you might consider using a set of functions (or macros) to approximate using default args:

// No default args
int foo3(int a, int b, int c)
{
    return ...;
}

// Default 3rd arg
int foo2(int a, int b)
{
    return foo3(a, b, 0);  // default c
}

// Default 2nd and 3rd args
int foo1(int a)
{
    return foo3(a, 1, 0);  // default b and c
}

Upvotes: 6

chaos
chaos

Reputation: 124335

No.

Upvotes: 9

unwind
unwind

Reputation: 400029

No.

Not even the very latest C99 standard supports this.

Upvotes: 20

Alex B
Alex B

Reputation: 84922

No, that's a C++ language feature.

Upvotes: 18

Related Questions