Ever wanted to be able to write C function calls with arguments named in the call?
Have you ever wished you could write
result = foo(.arg3 = xyz, .arg2 = abc);
as you can in some programming languages? If not then you’ve probably not met an API with functions that have a dozen arguments most of which take default values. There exist such APIs, and despite any revulsion you might feel about them, there’s often good reasons for the need for so many parameters that can be defaulted.
Well, you can get pretty close to such syntax, actually. I don’t know why, but I thought of the following hack at 1AM last Thursday, trying to sleep:
struct foo_args { type1 arg1; type2 arg2; type3 arg3; }; #define CALL_W_NAMED_ARGS3(result, fname, ...) \ do { \ struct fname ## _args _a = { __VA_ARGS__ }; (result) = fname(_a.arg1, _a.arg2, _a.arg3); } while (0) ... CALL_W_NAMED_ARGS3(res, foo, .arg2 = xyz, .arg3 = abc);
This relies on C99 struct initializer syntax and variadic macros, but it works. Arguments with non-zero default values can still be initialized since C99 struct initializer syntax allows fields to be assigned more than once.
If you use GCC’s statement expressions extension (which, incidentally, is supported by Sun Studio), you can even do this:
#define CALL_W_NAMED_ARGS3(fname, ...) \ ({ \ struct fname ## _args _a = { __VA_ARGS__ }; fname(_a.arg1, _a.arg2, _a.arg3); }) ... res = CALL_W_NAMED_ARGS3(foo, .arg2 = xyz, .arg3 = abc);
You can even define a macro that allows you to call functions by pointer, provided you have suitable typedefs:
struct foo_t_args { ... }; #define CALL_PTR_W_NAMED_ARGS3(ftype, func, ...) \ ({ \ struct ftype ## _args _a = { __VA_ARGS__ }; func(_a.arg1, _a.arg2, _a.arg3); }) ... foo_t f = ...; res = CALL_W_NAMED_ARGS3(foo_t, f, .arg2 = xyz, .arg3 = abc);
Useful? I’m not sure that I’d use it. I did a brief search and couldn’t find anything on this bit of black magic, so I thought I should at least blog it.
What’s interesting though is that C99 introduced initializer syntax that allows out of order, named field references (with missing fields getting initialized to zero) for structs (and arrays) but not function calls. The reason surely must be that while structs may not have unnamed fields, function prototypes not only may, but generally do have unnamed fields. (That and maybe no one thought of function calls with arguments named in the call parameter list).
You should check out how well this works out in Ada2005, far less convoluted !
Cheers,
Edward said this on January 12, 2010 at 05:11 |
I’m sure it is. Several other languages clearly support naming of arguments in function calls natively. Yet many of us are stuck using C. It’s been observed that most of what can be done in other languages can be done in C, with discipline. This is just one more thing that can be done in C. (Some things are hard to do in C in a way that still resembles C, namely closures and continuations; for continuations one ends up having to write in CPS, which is really suited only for compilers to generate.)
Nico said this on January 12, 2010 at 08:16 |
"I wouldn’t call it "stuck"; I’d feel very lucky if I could land a job where I did not have to program in Ruby or Python, but could program in C day in, day out, all day long.
UX-admin said this on January 12, 2010 at 12:45 |
True. Personally I love C. I like the fact that with discipline I can get what other languages give me, but with the cost being much clearer (not least because I don’t have to take all of what a higher level language would give me). But I do think it’s important to have multiple languages around, some high level and some low.
The particular API that made me think of this "hack" is the GSS-API, where you have functions like gss_init_sec_context(3GSS) that have a large number of arguments, most with reasonable default values. The GSS-API is famous for having large numbers of functions with large numbers of arguments that are not needed most of the time. I was thinking of ways to simplify use of it…
I’m torn about this idea though. On the one hand it could help developers, on the other hand, I wonder what developer reaction to this would be.
Nico said this on January 12, 2010 at 12:58 |