Dumb things to do in C: named arguments!

Question: can the following be valid C (in the same compilation unit):

draw_text( .x = 10, .text = "Hello Sailor" );
draw_text( .text = "Hello World" );
draw_text( .text = "Hello Pirate", .color = 0xff0000ff );

Yup. Up until very recently I didn’t know you could kinda use named arguments in C, but you can. Of course, like most C thing, it involves a few stupid tricks, but still, the end result is pretty nice.

Note: I didn’t come up with this by myself, but I can’t remember where I saw that first. If I do, I’ll update this and add the source.

The following piece of code illustrate how you could do that:

// This struct holds all the options that our function will need.
typedef struct {
    int x, y;
    const char *text;
    unsigned int color;
} DrawTextOpt;

// Our draw_text function implementation.
void draw_text_impl(DrawTextOpt opt) {
    // draw you text with the given color, at the specified location.
}

// And here is the trick (warning: it's uggly :D)
#define draw_text(...)           \
    draw_text_impl(              \
        (DrawTextOpt){           \
            .x = 0,              \
            .y = 0,              \
            .text = "",          \
            .color = 0xffffffff, \
            __VA_ARGS__          \
        }                        \
    )

Uggly right? But still, cool! You get default options, and you can just override the ones you need using named arguments! Worth noting: this will trigger warnings on (probably) most compilers because we might override initializers (like text is initialized to "" and will mostly like be replace in the var args because not many people want to draw empty strings)

It’s pretty easy to fix, at the expanse of a little more uggliness :p

// Those macros temporarily disable the warning that's triggered. Clang's provided
// here for example, Gcc and other compilers will probalby have their own way of doing it.
#ifdef __clang__
#define NAMED_ARGS_BEGIN             \
    _Pragma("clang diagnostic push") \
    _Pragma("clang diagnostic ignored \"-Winitializer-overrides\"")
#define NAMED_ARGS_END \
    _Pragma("clang diagnostic pop")
#endif

// Here's the updated version of draw_text which no longer issue any warnings:
#define draw_text(...)           \
    NAMED_ARGS_BEGIN             \
    draw_text_impl(              \
        (DrawTextOpt){           \
            .x = 0,              \
            .y = 0,              \
            .text = "",          \
            .color = 0xffffffff, \
            __VA_ARGS__          \
        }                        \
    )                            \
    NAMED_ARGS_END

It could probably be further modified to make it easier to define new functions without having to rewrite most of the boilerplate C macros, but I’ll leave that to whomever (if any) ‘s reading this post.

Leave a Reply

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