Created
March 29, 2011 02:05
-
-
Save ELLIOTTCABLE/891693 to your computer and use it in GitHub Desktop.
Sodomizing ISO C for fun and profit.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// The upshot: (in legal, sane(?) ISO C99) | |
type v = Namespace__foo_bar(.first = 123, .third = "non-default value!"); | |
/* We want a function (macro.) that appears to take “named arguments,” by preprocessing into a designated | |
* initializer directly at the position of the function call, post-processing. | |
*/ | |
// This portion is all, effectively, a neat way to define default, named arguments. | |
struct _namespace__foo_bar__ARGS { int first; double second; char* third; struct widget fourth; } | |
void _namespace__foo_bar__INITIALZE_ARGS(struct _namespace__foo_bar__ARGS a) { | |
if ((int)a.first == 0) a.first = 12; | |
if ((int)a.second == 0) a.second = 42.0; | |
if ((int)a.third == 0) strcpy(&a.third , "Default!") | |
if ((int)a.fourth == 0) memcpy(&a.fourth , &(struct widget){ .sub = NULL } | |
, sizeof(struct widget)); | |
return _namespace__foo_bar(a); } | |
static void _namespace__foo_bar(struct _namespace__foo_bar__ARGS a) { | |
// In here, we get to reference elements of `a`, our arguments struct, with default values initialized for us | |
return; } | |
// Finally, how you *actually call it*. | |
# define namespace__foo_bar(...) \ | |
_namespace__foo_bar__INITIALZE_ARGS((struct _namespace__foo_bar__ARGS){ __VA_ARGS__ })//; | |
/* Now, how to turn this all into one easy-to-use macro replacement? | |
* | |
* Well, easy-to-use is unlikely at best. But we can do something. We’ll have to use two different primary | |
* macros, one for declarations and one for the actual definition. This is because we can’t cleanly take | |
* unlimited numbers of named pairs of type-and-name, split them apart, and then construct them into | |
* default-definitions or struct-member-declarations as necessary. | |
* | |
* We also need some smaller macros, utilized as the variadic arguments to the primary macros, for the | |
* definitions and declarations of individual types. | |
*/ | |
# define VARIADIC_DECLARE(RETURNS, NAME, ...) \ | |
struct _ ## NAME ## __ARGS { __VA_ARGS__ }; \ | |
RETURNS _ ## NAME ## __INITIALZE_ARGS(struct _ ## NAME ## __ARGS a); \ | |
static RETURNS _ ## NAME ## (struct _ ## NAME ## __ARGS a); | |
# define VARIADIC_DEFINE(RETURNS, NAME, ...) \ | |
RETURNS _ ## NAME ## __INITIALZE_ARGS(struct _ ## NAME ## __ARGS a) { \ | |
(__VA_ARGS__); \ | |
return _ ## NAME ## (a); } \ | |
static RETURNS _ ## NAME ## (struct _ ## NAME ## __ARGS a)// { … } | |
/* I suspect I could wrap these into a much more compact API of macros, but I’m getting tired of this exercise | |
* already. You grasp the basic idea, even if the definition of these macros is unnecessarily verbose. */ | |
// “Header” arguments (used with `VARIADIC_DECLARE`.) | |
# define H( TYPE, NAME) TYPE NAME; | |
# define HD(TYPE, NAME, DEFAULT) TYPE NAME; | |
# define H_STR( TYPE, NAME) TYPE NAME; | |
# define HD_STR(TYPE, NAME, DEFAULT) TYPE NAME; | |
# define H_DATA( TYPE, NAME) TYPE NAME; | |
# define HD_DATA(TYPE, NAME, DEFAULT) TYPE NAME; | |
// “Implementation” arguments (used with `VARIADIC_DEFINE`.) | |
# define I( TYPE, NAME) (0)//; | |
# define ID(TYPE, NAME, DEFAULT) ( ((int)a. ## NAME == 0) ? (a. ## NAME = DEFAULT) : 0 )//; | |
# define I_STR( TYPE, NAME) (0)//; | |
# define ID_STR(TYPE, NAME, DEFAULT) ( ((int)a. ## NAME == 0) ? strcpy(&a. ## NAME , DEFAULT) : 0 )//; | |
# define I_DATA( TYPE, NAME) (0)//; | |
# define ID_DATA(TYPE, NAME, DEFAULT) ( ((int)a. ## NAME == 0) ? memcpy(&a. ## NAME , &(TYPE){ DEFAULT } , sizeof(TYPE)) : 0 )//; | |
// ==== THE USAGE | |
/* Caveat: I haven’t yet figured out a way to wrap this #define into the macro expansions above. I suspect | |
* there’s a way to do it involving #include, a way less ugly than the ones I’ve considered so far, but I’m not | |
* sure what that way is quite yet. The format doesn’t change, though, so for now, you can just copy-paste it. | |
*/ | |
// something.h | |
VARIADIC_DECLARE(void, namespace__foo_baz, int first; double second; char* third; struct widget fourth;) | |
# define namespace__foo_baz(...) _namespace__foo_baz__INITIALZE_ARGS((struct _namespace__foo_baz__ARGS){ __VA_ARGS__ })//; | |
// something.c | |
VARIADIC_DEFINE(void, namespace__foo_baz, ID(int, first, 12) | |
, ID(double, second, 42.0) | |
, ID(char*, third, "Default!") | |
, ID(struct widget, fourth, { .sub = NULL })) { | |
// Implementation of `namespace__foo_baz()`. | |
return; } | |
// elsewhere.c | |
# include "something.h" | |
int main(int argc, char const **argv) { | |
namespace__foo_baz(.second = 5, .fourth = { .sub = &carrot }); | |
return 0; } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment