Maybe your function formats and signs a message that could have quite a variety of fields.
int sign(struct key *key, const char *this, const char *that) { x_sign(key, this); ... } ... int result = sign(key, "This", "That");
and then you find yourself wanting to add const char* other.
Yeah... and there will be a lot of caller code to fixup (unless you want to use the other macro trick for default arguments, which I'll write up later).
Or will there?
struct sign_args { const char* this; const char* that; const char* other; }; int sign(struct key *key, struct sign_args *args) #define sign(key, ...) sign(key, &(struct sign_args){ __VA_ARGS__ }) { x_sign(key,args->this); ... }
and it is called the same way:
sign(key, "This", "That", "Other");
but you also get keyword arguments for free!
sign(key, .other = "Other"); // leaves this & that NULL
And this expands out to a valid compound literal expression. The temporary struct (an lvalue) is created (probably on the stack) and disposed of automatically at the end of the expression.
sign(key, &(struct sign_args){ .other = "Other" });
int batch_sign(struct key *key, struct sign_args *args);
int sign(struct key *key, struct sign_args *args) #define sign(key, ...) sign(key, &(struct sign_args){ __VA_ARGS__}) { x_with_unlocked_key(key, batch_sign, args); ... }
Is it efficient? Every time the args are passed, only a pointer is passed, so that can be efficient; but the struct does have to be created once and may be a little more work than passing a struct as an argument or passing the arguments.
The main points to note are that
The main points to note are that
- the original arguments should keep their same order at the start of the args struct
- as the macro has the same name as your function, be sure to define it AFTER your function
- the arguments are passed by pointer and so shared as they are tunnelled.
This can make it easy to pass back a temporary value from an auto-variable.
int sign(struct key *key, struct sign_args *args) #define sign(key, ...) sign(key, (struct sign_args){ __VA_ARGS__ }) { x_sign(key,args.this); ... }
it is called the same way:
sign(key, "This", "That", "Other");
but expands out slightly differently, passing the struct instead of a pointer to it, e.g.:
sign(key, .other = "Other"); // leaves this & that NULL
which this expands out to:
sign(key, (struct sign_args){ .other = "Other" });