Table of Contents:
Introduction
In a language like python you have the concept of **kwargs
as a function parameter. This special double *
syntax allows you to pass in named arguments captured as a dictionary, like the example below:
def print_item(**kwargs):
# kwargs is a dictionary
print(f"Item name: {kwargs['name']}")
if "kind" in kwargs:
print(f"Kind: {kwargs['kind']}")
print(f"Quantity: {kwargs['quantity']}")
print(f"Liters: {kwargs['liters']}")
print_item(name="Milk", quantity=2, liters=1)
# output:
# Item name: Milk
# Quantity: 2
# Liters: 1
This allows for a convenient way to pass arguments by their name in any order you'd like and have optional parameters without complicating the function signature.
What if we could do this in C? (spoiler: there is a way)
Implementation in C
Through the magic of the preprocessor we can accomplish this effect, optional parameters and all.
First lets define our item
structure.
struct item_t {
const char *name;
const char *kind;
int quantity;
float liters;
};
Next we'll define our print function like you would normally in C.
// name it with `__t` for the concrete function.
void print_item__t(struct item_t item) {
printf("Item name: %s\n", item.name);
if (item.kind != NULL) {
printf("Kind: %s\n", item.kind);
}
printf("Quantity: %d\n", item.quantity);
printf("Liters: %f\n", item.liters);
}
Now we can write the magically preprocessor part.
// name the macro what we want and use `...` to signify va_args
// Then call our function with a structure with default values
// and the va_args at the end to overwrite any values.
#define print_item(...) \
print_item__t((struct item_t){ \
.name = "default", \
.kind = NULL, \
.quantity = 0, \
.liters = 0, \
__VA_ARGS__ \
})
So lets break down this macro.
- First we define the macro name and specify we are using va_args with
...
as the parameter.
#define print_item(...) \
- Next we call the function with a structure of our
item_t
with default values.
print_item__t((struct item_t){ \
.name = "default", \
.kind = NULL, \
.quantity = 0, \
.liters = 0, \
- Last we pass in the
__VA_ARGS__
values at the end of our default structure. This works because C allows the later defined properties to overwrite earlier ones.
.liters = 0, \
__VA_ARGS__ \
})
Here's how all of this would be structured in actual files.
-
item.h
#ifndef ITEM_H
#define ITEM_H
struct item_t {
const char *name;
const char *kind;
int quantity;
float liters;
};
void print_item__t(struct item_t item);
#define print_item(...) \
print_item__t((struct item_t){ \
.name = "default", \
.kind = NULL, \
.quantity = 0, \
.liters = 0, \
__VA_ARGS__ \
})
#endif
-
item.c
#include "item.h"
#include <stdio.h>
void print_item__t(struct item_t item) {
printf("Item name: %s\n", item.name);
if (item.kind != NULL) {
printf("Kind: %s\n", item.kind);
}
printf("Quantity: %d\n", item.quantity);
printf("Liters: %f\n", item.liters);
}
Using our Implementation
Now we can use our macro similar to the python example in the introduction.
#include "item.h"
int main(void) {
print_item(.name = "Milk", .quantity = 2, .liters = 1);
return 0;
}
// output:
// Item name: Milk
// Quantity: 2
// Liters: 1
Conclusion
Through preprocessor magic we can achieve a syntax convenience with calling functions that have struct parameters as options. This can be extremely useful for functions with lots of optional parameters and\or default values. However, preprocessor magic is something that is either loved or hated so you may want to consider if it's worth implementing in your project or not.
Top comments (1)
You made a small mistake with the comments at the end (instead of
//
or/*
you used#
)I really liked the idea of making parameters optional, even though I know you mentioned creating a Python-like way to interact with parameters
This method allows for optional parameters, and if you think about it further, you can implement mandatory parameters as well
Speaking of C syntax tricks, I once played around with using the dot "similar to how namespaces are used."
My favorite example is a base64 library:
And now we can use
b64.encode
andb64.decode
that looks cool