Last active
August 29, 2015 14:07
-
-
Save mkmik/988a968c5333620d28e7 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
| /* | |
| * 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