-
-
Save mkmik/78fad4daace38a3a6cef to your computer and use it in GitHub Desktop.
This file contains hidden or 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
/* | |
* This is dummy port of the Go option pattern to C. | |
* | |
* The option pattern is described somewhat obscurely in | |
* http://commandcenter.blogspot.nl/2014/01/self-referential-functions-and-design.html | |
* | |
* In Go it already suffers from some boilerplate explosion, but it's somewhat manageable. | |
* Here it's even worse, so I'm afraid this is just to be used for educational purposes. | |
* | |
* To be fair, it should be possible to throw even some more preprocessor magic to | |
* make it shorter and to avoid relying on some conventions in the naming of functions and types. | |
* | |
* However the greater weakness is the fact that C variadic arguments cannot be typed, | |
* while in Go you can enforce a single type for all subsequent varargs. | |
*/ | |
#include <stdarg.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
// This hack allows us to count the number of args passed to a macro. | |
#define VA_NUM_ARGS(...) \ | |
(sizeof(#__VA_ARGS__) == sizeof("") \ | |
? 0 : VA_NUM_ARGS_IMPL(__VA_ARGS__, 16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1)) | |
#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,N,...) N | |
typedef void (*opt_mutator_t)(void *args, void *opts); | |
#define MUTATE_OPTIONS(last_arg, opts) {\ | |
va_list argp; \ | |
va_start(argp, last_arg); \ | |
for(int i=0; i<nargs; i++) { \ | |
opt_mutator_t *f = va_arg(argp, opt_mutator_t*); \ | |
(*f)(f, &(opts)); \ | |
free(f); \ | |
}\ | |
} | |
#define DECLARE_OPTION_MUTATOR(m, mut) \ | |
mut ## _t* m = malloc(sizeof(mut ## _t)); \ | |
*(opt_mutator_t*)m = (opt_mutator_t)(mut) | |
#define BEGIN_OPTION(name, m, ...) \ | |
name ## _opt_mutator_t* name(__VA_ARGS__) { \ | |
DECLARE_OPTION_MUTATOR(m, name ## _opt_mutator); | |
#define END_OPTION(m) return (m); } | |
// ------------ ns_send | |
typedef struct { | |
int deadline; | |
int n_headers; | |
} ns_send_opts_t; | |
// ns_header option | |
typedef struct { | |
opt_mutator_t mutator; | |
char *k; | |
char *v; | |
} ns_header_opt_mutator_t; | |
void ns_header_opt_mutator(ns_header_opt_mutator_t *args, ns_send_opts_t *opts) { | |
printf("Adding header %s:%s\n", args->k, args->v); | |
// just an example | |
opts->n_headers++; | |
} | |
BEGIN_OPTION(ns_header, m, char* k, char* v) { | |
m->k = k; | |
m->v = v; | |
} | |
END_OPTION(m); | |
// ns_deadline option | |
typedef struct { | |
opt_mutator_t mutator; | |
int deadline; | |
} ns_deadline_opt_mutator_t; | |
void ns_deadline_opt_mutator(ns_deadline_opt_mutator_t *args, ns_send_opts_t *opts) { | |
opts->deadline = args->deadline; | |
} | |
BEGIN_OPTION(ns_deadline, m, int d) { | |
m->deadline = d; | |
} | |
END_OPTION(m) | |
// ns_send | |
#define ns_send(addr, body, ...) va_send(addr, body, VA_NUM_ARGS(__VA_ARGS__), ## __VA_ARGS__) | |
void va_send(char* addr, char* body, int nargs, ...) { | |
// some default values | |
ns_send_opts_t opts = {-1, 0}; | |
MUTATE_OPTIONS(nargs, opts); | |
printf("sending %s to %s, deadline: %d, num headers: %d\n", body, addr, opts.deadline, opts.n_headers); | |
} | |
int main() { | |
ns_send("foo", "x", ns_header("content-type", "json"), ns_header("auth-key", "hunter2")); | |
ns_send("foo", "x", ns_header("content-type", "poo")); | |
ns_send("bar", "x", ns_deadline(3600), ns_header("foo", "bar")); | |
ns_send("bar", "x", ns_deadline(3600), ns_header("foo", "bar")); | |
ns_send("bar", "noopts"); | |
// missing argument caught by the compiler | |
// ns_send("bar", ns_deadline(3600), ns_header("foo", "bar")); | |
// not caught by the compiler. | |
// ns_send("bar", "x", "y"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment