Skip to content

Instantly share code, notes, and snippets.

@mkmik
Last active August 29, 2015 14:07
Show Gist options
  • Save mkmik/988a968c5333620d28e7 to your computer and use it in GitHub Desktop.
Save mkmik/988a968c5333620d28e7 to your computer and use it in GitHub Desktop.
/*
* Quick and dirty optional option struct via function overload via preprocessor trick.
*
* It contains a (pretty ugly) workaround that allows it to work on preprocessor that don't understand
* ", ##" GNU extension. It requires the preprocessor to accept zero length varargs,
* which is apparently implemented elsewhere.
*/
#include <stdio.h>
/////////////////////////////////////
// Magic library
// expand to prefix + suffix where suffix is either s0 or s0 depending whether the vararg list is the same length as
// args or 1 item longer.
// usage: #define function M_DISPATCH(function, _one, _two, M_FIXED_ARGS(arg0, arg1), __VA_ARGS__)
//
#define M_DISPATCH(prefix, s0, s1, args, ...) M_CONCAT(prefix, VA_SUFFIX(s0, s1, GNU_ZERO_VARIADIC(__VA_ARGS__)))(args GNU_ZERO_VARIADIC(__VA_ARGS__))
// call it to declare the list of the fixed arguments of the function
#define M_FIXED_ARGS(...) __VA_ARGS__
// returns either a or b depending of whether it's called by 1 or 2 vararg arguments
// note: not zero, you have pass a dummy first argument for it to work if you want to
// discriminate between 0 or 1 parameters.
#define __BAD _ARG_LIST_TOO_LONG_RTFM
#define VA_SUFFIX(a,b,...) VA_SUFFIX_IMPL(__VA_ARGS__, __BAD, __BAD, __BAD, b, a)
#define VA_SUFFIX_IMPL(_1,_2,_3,_4,_5, N,...) N
// double indirection needed to concatenate two macro expansions
#define M_CONCAT(x,y) M_PASTER(x,y)
#define M_PASTER(x,y) x ## y
/////////////////////////////////////
// hack to detect empty vararg
// based on a majestic hack found in http://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
// kudos to Jens Gustedt.
#define _ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15
#define _COMMA_TOKEN_
#define HAS_COMMA(...) _ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
#define _TRIGGER_PARENTHESIS_(...) ,
#define ISEMPTY(...) \
_ISEMPTY( \
/* test if there is just one argument, eventually an empty \
one */ \
HAS_COMMA(__VA_ARGS__), \
/* test if _TRIGGER_PARENTHESIS_ together with the argument \
adds a comma */ \
HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__), \
/* test if the argument together with a parenthesis \
adds a comma */ \
HAS_COMMA(__VA_ARGS__ (/*empty*/)), \
/* test if placing it between _TRIGGER_PARENTHESIS_ and the \
parenthesis adds a comma */ \
HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/)) \
)
#define _IS_EMPTY_CASE_0001 ,
#define PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
#define _ISEMPTY(_0, _1, _2, _3) HAS_COMMA(PASTE5(_IS_EMPTY_CASE_, _0, _1, _2, _3))
////////////////////////////////////
// based on Jens Gustedt's code to detect an empty vararg we can do this:
#define NO_COMMA_IF_EMPTY(...) M_CONCAT(COND_COMMA_, ISEMPTY(__VA_ARGS__))
#define COND_COMMA_0 ,
#define COND_COMMA_1
// this one is the replacemente for gnu-zero-variadic-macro-arguments GNU extension
#define GNU_ZERO_VARIADIC(...) NO_COMMA_IF_EMPTY(__VA_ARGS__) __VA_ARGS__
// end magic library
////////////////////////////////////
////////// REAL CODE
// ---- ns_send
typedef struct {
int deadline;
int n_headers;
} ns_send_opts_t;
// define a dispatcher that will invoke ns_send_legacy if invoked with only two parameters
// and ns_send_opts if invoked with three parameters.
#define ns_send(addr, body, ...) M_DISPATCH(ns_send, _legacy, _opts, M_FIXED_ARGS(addr, body), __VA_ARGS__)
void ns_send_opts(char* addr, char* body, ns_send_opts_t opts) {
printf("sending %s to %s, deadline: %d, headers: %d\n", addr, body, opts.deadline, opts.n_headers);
}
void ns_send_legacy(char* addr, char* body) {
ns_send_opts(addr, body, (ns_send_opts_t){0});
}
////////// USAGE
int main() {
ns_send("x", "addr");
ns_send_opts_t opts = {.deadline = 1};
ns_send("x", "addr", opts);
// not super nice, but parseable error message indicating that there are too many arguments
// ns_send("x", "addr", opts, x);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment