Preprocessor magic: Default Arguments in C

This is a post explaining how one can utilize (or “abuse” according to some people) the C preprocessor in C99 compliant compilers in order to achieve default argument functions in the C programming language. Two methods are presented, one that has a minor overhead and another one that uses the preprocessor to eliminate that overhead along with sourcecode examples for both.

This post is for programmers who like C or for one reason or another can’t use anything else but C in one of their projects. The advantages of having default arguments is not something that needs convincing. It’s just very nice and convenient to have them. C++ offers the ability to define them but C under the C99 standard has no way to allow it. In this post I will detail two ways I know of implementing default arguments in C. If a reader happens to know additional ways please share in the comments

Suppose we have a struct that contains some data and we want to initialize it

//! The struct we want to initialize
typedef struct foo
{
   //! The first 3 parameters get initialized during the init function
   int an_int;
   char a_char;
   float a_float;
  //! The other are just parameters used later 
   char type;
   char runTimeFlag;
}foo;

Method 1

The first method is my favorite and works in C99 compliant C-code. The code is by no means originally my invention and all credit goes to Jens Gustedt. He details this method and other amazing preprocessor trick in his blog. He also has created a collection of headers that one can include in his project that provides all of the tricks he writes about. It is called P99 and can be found here.

Using this method we have to define 3 different functions for all the different ways of initializing the default arguments.

//! Let's define the default value for the 3rd argument
#define DEFAULT_3    3.14
//! Let's define the default value for the 2nd argument
#define DEFAULT_2    42
//! Let's define the default value for the 1st argument
#define DEFAULT_1   1337
//! A function to initialize a foo with all 3 arguments 
foo* def_foo_init3(int arg1,char arg2,float arg3)
{
   foo* ret = malloc(sizeof(foo));
   ret->an_int = arg1;
   ret->a_char = arg2;
   ret->a_float = arg3;
 
   return ret;
}
//! A function to initialize a foo with only the first 2 arguments
foo* def_foo_init2(int arg1,char arg2)
{
   //call the full initializer but with the default value for the 3rd argument
   return def_foo_init3(arg1,arg2,DEFAULT_3);
}
//! A function to initialize a foo with only the first argument
foo* def_foo_init1(int arg1)
{
   //call the full initializer but with the default value for the 2nd and 3rd argument
   return def_foo_init3(arg1,DEFAULT_2,DEFAULT_3);
}
//! A function to initialize a foo with no arguments
foo* def_foo_init0()
{
   //call the full initializer with default values
   return def_foo_init3(DEFAULT_1,DEFAULT_2,DEFAULT_3);
}

Now you might be thinking, and so what? What if we have different functions that use the default arguments in C if we have to call the appropriate function ourselves? You would be right to ponder that, and here is where the preprocessor magic comes in. Define all these macros in a header. Alternatively you can use the P99 library that has them defined for you already.

//! A macro used to count the number of arguments. Can go up to 64 which according to the original author is the limit for C
#define RF_ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15
//! Just a macro that has a reverse sequence of the 16 numbers above
#define RF_ARG16_RSEQ()    15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0
 
//! This macro is one of those which do the magic. It returns the number of arguments that have been passed as parameters to the macro
//! @warning This does not work on an empty list of arguments. To circumvent this problem we will see a trick further down
#define GET_NARG_NOEMPTY(...)          RF__NARG_NOEMPTY_IMP(__VA_ARGS__,RF_ARG16_RSEQ())
#define RF_NARG_NOEMPTY_IMP(...)       RF_ARG16(__VA_ARGS__)

So now if you use the GET_NARG_NOEMPTY() on a list of arguments it will return the number of arguments given. But how does it work? Well imagine this test case. Say we pass a list of 3 arguments (54,23,2.345) inside the macro like below

GET_NARG_NOEMPTY(54,23,2.345) // Will unfold to==>
 RF_NARG_NOEMPTY_IMP(54,23,2.345,RF_ARG16_RSEQ()) // Will unfold to==>
 RF_NARG_NOEMPTY_IMP(54,23,2.345,   15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0) // Will unfold to==>
 RF_ARG16(54,23,2.345,   15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0) // And from the definition of RF_ARG16 we get the 16th number in the list and this will unfold to ==>
3

Pretty amazing right? I thought so too when I first learned that trick. But as we mentioned before this has some limitations. Specifically it will not work for an empty list as it will still return 1. Quoting Jens Gustedt directly “So in fact GET_NARG_NOEMPTY is cheating. It doesn�t count the number of arguments that it receives, but returns the number of commas plus one. In particular, even if it receives an empty argument list it will return 1″

To rectify that we will define some additional macros below. This is the nicest part of the trick in my opinion and this is how I came to learn about Jens Gustedt’s blog by trying to find a way to make this macro work for any number of arguments, including the no arguments case

//! A macro that pastes 2 tokens together using the ## operator
#define RF_PASTE2(__0,__1)  __0 ## __1
//! A macro that pastes 5 tokens together using the ## operator
#define RF_PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
 
//! This is a macro using the same functionality as above to see if the argument list has a comma. Returns 1 if it does and 0 if not
#define HAS_COMMA(...) RF_ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
 
//! This one is essential to the succesfull working of this trick. Check below for the usage explanation
#define RF_TRIGGER_PARENTHESIS_(...) ,
 
//! This is the soul of the macros checking if the arguments list is empty
//! It tests for 4 different cases detailed in the comments below. Each one returns 0 for false and 1 for true
#define IS_EMPTY_NARG(...)                                                    \
_ISEMPTY(                                                               \
            /* test if there is just one argument, eventually an empty    \
	             one */                                                     \
	          HAS_COMMA(__VA_ARGS__),                                       \
	          /* test if RF_TRIGGER_PARENTHESIS_ together with the argument   \
	             adds a comma */                                            \
	          HAS_COMMA(RF_TRIGGER_PARENTHESIS_ __VA_ARGS__),                 \
	          /* test if the argument together with a parenthesis           \
	             adds a comma */                                            \
	          HAS_COMMA(RF_VA_ARGS__ (/*empty*/)),                           \
	          /* test if placing it between _TRIGGER_PARENTHESIS_ and the   \
	             parenthesis adds a comma */                                \
	          HAS_COMMA(RF_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/))      \
	          )
 
//! This macro tests the results of the above macro. The 4 different arguments are the results of the 4 different checks
#define RF_ISEMPTY(_0, _1, _2, _3) HAS_COMMA(RF_PASTE5(RF_IS_EMPTY_CASE_, _0, _1, _2, _3))
//! This is the definition of the only case of the above checks we care about
#define RF_IS_EMPTY_CASE_0001 ,

So what’s happening here? With RF_TRIGGER_PARENTHESIS_() macro we are taking advantage of the fact that a function macro that does not get followed by its parentheses will be left alone. Notice how it is used. By putting the arguments between the macro and its parentheses as done in the 4th check above we basically make sure that the macro will work only if there are no arguments

RF_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/) //the RF_TRIGGER_PARENTHESIS__ macro will only fire up and unfold to a comma if __VA_ARGS__ is empty

The IS_EMPTY_NARG(…) macro tests for 4 different special cases that we first of all have to take care of. These are

  1. The argument list has a comma already (Retuns 1 if yes and 0 if not)
  2. RF_TRIGGER_PARENTHESIS_ together with the argument add a comma (Retuns 1 if yes and 0 if not)
  3. The argument together with a parentheses add a comma (Retuns 1 if yes and 0 if not)
  4. The argument is empty and so being placed between RF_TRIGGER_PARENTHESIS_ and a parentheses adds a comma (Retuns 1 if yes and 0 if not)

So the IS_EMPTY_NARG(…) macro tests for the above cases and returns _IS_EMPTY_CASE_XXXX depending on the result of the checks by pasting the tokens. The only results that we actually care about it RF_IS_EMPTY_CASE_0001 which basically means that the argument list is empty and that is why it is defined above.

Now we are almost there. Since we have a way to detect if the argument list is empty or not and also a way to get the number of arguments it’s only a matter of having a macro to implement both checks and return us the results. So finally define the macros given below

//! Two different helper macro that evaluate if the variadic list is empty or not.
#define RF_NARG_EMPTY_1(__V__) 0
#define RF_NARG_EMPTY_0(__V__) __V__
 
//! THIS is the macro that actually counts the number of arguments that are passed to it and returns it. Works for empty list too.
//! >>THIS DOES ALL THE WORK<<
#define GET_NARG(...) RF_NARG__1(IS_EMPTY_NARG(__VA_ARGS__), GET_NARG_NOEMPTY(__VA_ARGS__))
    //! Helper macro. Takes in the result of if the variadic list is empty (1) or not (0) and pastes it to RF_NARG_EMPTY_
    #define RF_NARG__1(B, __V__) __NARG__2(RF_PASTE2(RF_NARG_EMPTY_, B), __V__)
    //! Helper macro. Basically decides what RF_NARG returns, which is either __V__ the value of GET_NARG_NOEMPTY or 0 in the case of NARG_EMPTY() evaluating to 0
    #define RF_NARG__2(B, __V__) B(__V__)

So let’s see a working example now. Suppose we have an empty argument list (). Let’s pass it to our macro and see how and why it works

GET_NARG(/*empty*/) //==>unfolds to
RF_NARG__1(IS_EMPTY_NARG(/*empty*/), GET_NARG_NOEMPTY(/*empty*/)) //==>unfolds to
//as stated from the previous example the GET_NARG_NOEMPTY case does not work for empty arguments so returns 1 here which is wrong
//but the IS_EMPTY_NARG also returns 1 which shows that the arg list is empty 
RF_NARG__1(1, 1) //==>unfolds to
RF_NARG__2(RF_PASTE2(RF_NARG_EMPTY_,1), 1) //==>unfolds to
RF_NARG__2(RF_NARG_EMPTY_1, 1) //==>unfolds to
RF_NARG_EMPTY_1(1) //==>unfolds to
 0

So we get the result that the argument list is empty! Pretty neat huh? Also in the case that IS_EMPTY_NARG() returned 0 which meant that the argument list was not empty then the results would have finally unfolded to RF_NARG_EMPTY_0(ACTUAL_NUM_OF_ARGS) which would return the actual number of arguments.

So finally let’s see how to apply this to our foo struct example. We have already defined the 3 different functions and each one uses default arguments where needed so it’s only a matter of calling the correct function where needed. So to close this method define the macros below. Note that these macros can be used for anything not just the foo example

//! This macro selects the function name according to the arguments given and passes both the name and the arguments to the next macro
#define  RF_SELECT_FUNC(FUNCNAME,ARGSN,...)   RF_RUN_FUNC( RF_PASTE2(FUNCNAME,ARGSN),__VA_ARGS__)
//! This macro runs the function take from select function with the given argument
#define  RF_RUN_FUNC(FUNCNAME,...)   FUNCNAME(__VA_ARGS__)
//! This is the only example specific macro which calls the appropriate _foo_init function depending on the number of arguments
#define foo_init(...) RF_SELECT_FUNC(def_foo_init,GET_NARG(__VA_ARGS__),__VA_ARGS__)

As you can see from the code above it’s just a matter of getting the correct number of arguments by the “magic macro” GET_NARG() defined above and then pasting that to the function name to choose the correct function and then pass the arguments to that function. This ofcourse has the prerequisite that you have defined your functions like I did in my example but it’s quite easy to alter this when/if needed.

Method 2

This post is getting a lot bigger than I initially expected so I am sorry but bear with me. The second method is quite different from the first and does not use the preprocessor that much. Instead we have to define an argument list with the parameters like below:

//! This is the argument list for initializing a foo object
typedef struct foo_args
{
   int arg1;
   char arg2;
   float arg3;
}foo_args;

After that we define a function that initializes foo with the foo argument list and a macro that takes in variadic arguments and calls said function.

//! This is the function that initializes a foo object by taking in a foo argument list
foo* def_foo_arg_init(foo_args args)
{
  int arg1 = (args.arg1 != 0) ? args.arg1 : DEFAULT_1;
  char arg2 = (args.arg2 != 0) ? args.arg2 : DEFAULT_2;
  float arg3 = (args.arg3 != 0) ? args.arg3 : DEFAULT_3;
 
  return def_foo_init3(arg1,arg2,arg3);
}
 
//! This is the macro that does all the work and uses a quite smart trick, explanation follows below
#define foo_init(...)  def_foo_arg_init((foo_args){__VA_ARGS__})

The foo_init() macro takes a variable number of arguments just like Method 1 but in this case here we put all these arguments into a braces enclosed list {…} and and the compound literals are interpreted as an object of foo_args. What this basically achieves is to populate the foo_args object with the parameters values and if there are not enough parameters pad it with zeroes. So when the def_foo_arg_init function is called with the foo_args object it checks each of the arguments of the list if it has a value (is not zero) and if it does, keeps that value or if it’s zero it then uses the default value.

One obvious and huge disadvantage of this method is that you can’t use it to give value of 0 to parameters unless it’s also the default value. This is the main reason this method is not my preferred one. On the other hand many people argue that 0 should not be a value and should always state the absence of a value in a program.

The advantage of this method is that you can basically do A LOT of processing of the arguments by doing some work inside _foo_arg_init and by having a smart foo_args object. You can actually process cases where the arguments are not given in the series you expect by having paddings inside the foo_args object e.t.c. The limit is ,as always, the programmer’s imagination.

Conclusions

This post became a lot bigger than I expected. I presented two methods I know for implementing default arguments in the C language which are compliant with the C99 standard. Credit for the first method goes to Jens Gustedt and for the second method I am afraid I don’t know the original author. I personally prefer the first method even though in cases where I want to accept arguments in any order and do lots of processing on them I use the second one. Another option is to combine both of them by using the GET_NARG() macro to also know the number of arguments passed to the function when using the second method. If you know of any other methods please share with me in the comments. I would be ecstatic to learn new ways to accomplish them. Also if you find any mistakes let me know so that I can correct them.

Also if the post caught your interest and you love C programming in general why not check my work in the Refu Library? It currently needs more contributors and people who are able and willing to provide feedback! You could be one of them 🙂

17 thoughts on “Preprocessor magic: Default Arguments in C”

  1. you can’t use leading underscore to name your functions, such identifiers are reserved.

  2. yes you are almost right! All names (macros/functions) beginning with either
    1) double leading underscore __
    2) single underscore followed by a capital letter

    are reserved for the implementation

    Thanks for pointing it out. Will fix it now

  3. It’s possible that I’m to blame for method 2 (see http://stackoverflow.com/a/2828263/48015 ), but there’s probably prior art – after all, C99 has been around for some time…

    Some minor corrections: What you called ‘casting a list’ is the syntax for compound literals – there’s no casting involved.

    Also, you shouldn’t use leading underscores – such names are reserved, and double underscores or leading underscores followed by uppercase letters are especially bad as the former is traditionally used for compiler intrinsics and language extensions, the latter for new C language keywords (eg _Bool, _Generic, _Static_assert, …) – use trailing underscores instead.

  4. Hello Cristoph. Thank you a lot for pointing it out. The moment I was writing it, I knew it sounded off. It’s good to know the proper way to refer to arguments inside braces. Fixed the post.

    As for the underscores, it is as you say. Refer to my comment 1 minute before you posted yours.

    Thanks a lot for your comments!

  5. Just as a side note: your second version can be used as something I would call “call via name” or something like that, when using designated initializers. foo_init(.arg1 = 5, .arg3 = 37) should work fine.

  6. I’m a begginer in C but I like to read advanced texts beyond my understanding! One day the pieces will fall together.

  7. @Jens happy to see you finding my blog. You have really inspired me with P99. I was working with C++ for many years and for the last 1.5 year I have switched to C, trying to find something less restricted, providing more freedom and power to the programmer. Needless to say that features such as default arguments are something I still miss from C++, that’s why I am interested in harnessing the power of the preprocessor for that … and maybe other things too.

    As for your comment, what do you mean exactly? When the compound literal gets put into the foo_args , use an intermediate macro that will take advantage of designated initializers and place default values for those who are not defined from the designated initializers?

    @Construcao: Hello, hope the post can be helpful. C is beautiful and you will have a blast learning it.

  8. Hm, probably it was not clear enough. What I mean that with your macro you can extend C’s calling convention such that you don’t have to place function parameters in the arbitrary order of declaration of the function (or your struct), but that you can use the .arg1 etc notation to give any subset of the parameters in any order you like.

  9. Oh I see what you mean now. Basically taking advantage of the compound literals, we can actually combine the macro and the C99 designated initializer list feature so that it also works for function calls.

    Pretty nice. Gotta think of applications for that now. Thanks for the input.

  10. Much simpler way to count args in c99:
    #define ARG_16(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,…) q
    #define COUNT(…) ARG_16(x,##__VA_ARGS__,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)

    This works on the fact that foo,##__VA_ARGS__ expands to foo without the comma.

  11. Hello Jason. Thanks for the input. I got a few questions for that. So you add one more variable in the ARG_16 macro so that it becomes 17.

    In the COUNT(…) macro definition I don’t understandwhat x stands for and how is it supposed to get a value since there is no other argument inside COUNT(…) other than the __VA_ARGS__.

    I don’t really understand where it’s simpler. Can you please elaborate more? Since what I see is the same thing only with 1 more argument in ARG_16. But I can’t understand the purpose of x in the above code.

    If you somehow are trying to combine the name of the function with the number of arguments so early in the stage, then how would this handle the no arguments case?

  12. “This works on the fact that foo,##__VA_ARGS__ expands to foo without the comma”

    this is a GCC extension

  13. @Leftris

    The COUNT macro now handles the empty arglist case for you, which is how it is simpler. Most of the complexity in the original version was handling the 0 arguments case.

  14. Also, the x is just a throwaway symbol. You could replace it with just about anything and it would work just fine.

  15. Jason I am afraid you are wrong. The zero arguments list is not handled by your example in every case.

    Let me explain why. I suppose you are using the GCC compiler. As Gregory said above you are using a compiler specific extension. So for your case it works. Incidentally I also use gcc version 4.4.1 and it works for me too but in this post I wanted to analyze methods that are actually portable. Why don’t you try to run it in MSVC and see the results? COUNT() will return 1.

    Just take a look at this page from the GCC manual
    http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html

    I am specifically quoting:
    “As an extension, GCC permits you to omit the variable arguments entirely when you use a variable argument macro. This is forbidden by the 1999 C standard, and will provoke a pedantic warning with GCC 3.0. Previous versions accepted it silently. ”

    So as you can see this method is non-portable and does not work except for specific GCC versions and since its not standard C they might even change it to conform to the C standard in the future and such a method would break.

  16. Pingback: Deafult Arguments
  17. VERY nice and enlightening, but I still prefer Obj-C 😉 named parameters are really great.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.