Named parameters in C

C99 introduced the concept of designated initializers, that allows to initialize a structure using the name of the fields, like this:

struct MyStruct {int x; float y;};
struct MyStruct a = {
    .x = 10,
    .y = 3.6,
};

Here is a C macro that extends this syntax to function calls.

I present it as a curiosity, and I really wouldn't advise anybody to actually use it in a project (except maybe for very special cases, like for example emulating the interface of a language that accepts named arguments).

The macro is using a few tricks, notably the 'foreach' as described in this stack overflow question.

[Edit] As some people pointed out on the hacker news thread, the macro also uses a statement expression, which is a GNU C extension, and so it would probably not work with MSVC (I am not going to try).

#define FE_0(a, a0, X)     a0(0, X)
#define FE_1(a, a0, X, ...) a(1, X)FE_0(a, a0, __VA_ARGS__)
#define FE_2(a, a0, X, ...) a(2, X)FE_1(a, a0, __VA_ARGS__)
#define FE_3(a, a0, X, ...) a(3, X)FE_2(a, a0, __VA_ARGS__)
#define FE_4(a, a0, X, ...) a(4, X)FE_3(a, a0, __VA_ARGS__)
#define GET_MACRO(_0, _1, _2, _3, _4, NAME,...) NAME
#define FOR_EACH(a, a0, ...) \
    GET_MACRO(__VA_ARGS__, FE_4, FE_3, FE_2, FE_1, FE_0) \
              (a, a0, __VA_ARGS__)

#define ARGS_STRUCT_ATTR(n, attr) union {attr, _##n;};

#define ARGS_STRUCT(...) \
    struct { \
        FOR_EACH(ARGS_STRUCT_ATTR, ARGS_STRUCT_ATTR, \
                 __VA_ARGS__) \
    }

#define ARGS_PASS(n, attr) _args._##n,
#define ARGS_PASS0(n, attr) _args._##n
#define PASS_STRUCT(...) \
    FOR_EACH(ARGS_PASS, ARGS_PASS0, __VA_ARGS__)

#define CALL_NAMED_ARGS(func, args, ...) ({ \
    ARGS_STRUCT args _args = {__VA_ARGS__}; \
    func(PASS_STRUCT args); \
    })

And here is how we can use it:

// A normal C function.
float func(int x, float y)
{
    printf("%d %f\n", x, y);
    return x + y;
}

// func2 will call `func` using named arguments.
#define func2(...) \
    CALL_NAMED_ARGS(func, (int x, float y), __VA_ARGS__)

int main()
{
    float ret = func2(.y = 1.5, .x = 20);
}

For the curious, here is what the macro expends to:

float ret = ({
    struct {
        union {int x, _1;};
        union {float y, _0;};
    } _args = {
        .y = 1.5,
        .x = 20
    };
    func(_args._1,_args._0);
});

The macro can only support up to 5 arguments (but, could easily be extended for more). One interesting thing is that if we omit a parameter it will be automatically set to 0. So let say you have a function like:

void func(int x0, int x1, int x2, int x3, int x4);

And you want to call it by setting most of the arguments to 0. You could do:

#define func2(...) CALL_NAMED_ARGS(func, \
    (int x0, int x1, int x2, int x3, int x4), \
    __VA_ARGS__)

func2(.x0 = 1);             // All 0 except x0
func2(.x1 = 4, .x4 = 2);    // All 0 except x1 and x4
func2();                    // All 0.

If you liked this, you can follow me on twitter: @guillaumecherea