diff --git a/include/nm-macros-internal.h b/include/nm-macros-internal.h index 405051f382..c619da2142 100644 --- a/include/nm-macros-internal.h +++ b/include/nm-macros-internal.h @@ -46,6 +46,31 @@ /********************************************************/ +/* http://stackoverflow.com/a/2124385/354393 */ + +#define NM_NARG(...) \ + _NM_NARG(__VA_ARGS__,_NM_NARG_RSEQ_N()) +#define _NM_NARG(...) \ + _NM_NARG_ARG_N(__VA_ARGS__) +#define _NM_NARG_ARG_N( \ + _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ + _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ + _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ + _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ + _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ + _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ + _61,_62,_63,N,...) N +#define _NM_NARG_RSEQ_N() \ + 63,62,61,60, \ + 59,58,57,56,55,54,53,52,51,50, \ + 49,48,47,46,45,44,43,42,41,40, \ + 39,38,37,36,35,34,33,32,31,30, \ + 29,28,27,26,25,24,23,22,21,20, \ + 19,18,17,16,15,14,13,12,11,10, \ + 9,8,7,6,5,4,3,2,1,0 + +/********************************************************/ + #if defined (__GNUC__) #define _NM_PRAGMA_WARNING_DO(warning) G_STRINGIFY(GCC diagnostic ignored warning) #elif defined (__clang__) @@ -85,21 +110,66 @@ /* macro to return strlen() of a compile time string. */ #define STRLEN(str) ( sizeof ("" str) - 1 ) -#define NM_IN_SET(x, y, ...) \ - ({ \ - const typeof(y) _y = (y); \ - typeof(_y) _x = (x); \ - unsigned _i; \ - gboolean _found = FALSE; \ - for (_i = 0; _i < 1 + sizeof((typeof(_x)[]) { __VA_ARGS__ })/sizeof(typeof(_x)); _i++) { \ - if (((typeof(_x)[]) { _y, __VA_ARGS__ })[_i] == _x) { \ - _found = TRUE; \ - break; \ - } \ - } \ - _found; \ +/********************************************************/ + +#define _NM_IN_SET_EVAL_1(op, x, y1) \ + ({ \ + typeof(x) _x = (x); \ + ( (_x == (y1)) \ + ); \ }) +#define _NM_IN_SET_EVAL_2(op, x, y1, y2) \ + ({ \ + typeof(x) _x = (x); \ + ( (_x == (y1)) \ + op (_x == (y2)) \ + ); \ + }) + +#define _NM_IN_SET_EVAL_3(op, x, y1, y2, y3) \ + ({ \ + typeof(x) _x = (x); \ + ( (_x == (y1)) \ + op (_x == (y2)) \ + op (_x == (y3)) \ + ); \ + }) + +#define _NM_IN_SET_EVAL_4(op, x, y1, y2, y3, y4) \ + ({ \ + typeof(x) _x = (x); \ + ( (_x == (y1)) \ + op (_x == (y2)) \ + op (_x == (y3)) \ + op (_x == (y4)) \ + ); \ + }) + +#define _NM_IN_SET_EVAL_5(op, x, y1, y2, y3, y4, y5) \ + ({ \ + typeof(x) _x = (x); \ + ( (_x == (y1)) \ + op (_x == (y2)) \ + op (_x == (y3)) \ + op (_x == (y4)) \ + op (_x == (y5)) \ + ); \ + }) + +#define _NM_IN_SET_EVAL_N2(op, x, n, ...) _NM_IN_SET_EVAL_##n(op, x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_N(op, x, n, ...) _NM_IN_SET_EVAL_N2(op, x, n, __VA_ARGS__) + +/* does not do short-circuit evaluation to get a more function-like behavior + * ("|" instead of "||"). Use NM_IN_SET_SC() if you want that */ +#define NM_IN_SET(x, ...) _NM_IN_SET_EVAL_N(| , x, NM_NARG (__VA_ARGS__), __VA_ARGS__) + +/* "SC" stands for "short-cirtuit". It will only evaluate the arguments + * until a match is found. */ +#define NM_IN_SET_SC(x, ...) _NM_IN_SET_EVAL_N(||, x, NM_NARG (__VA_ARGS__), __VA_ARGS__) + +/*****************************************************************************/ + #define NM_PRINT_FMT_QUOTED(cond, prefix, str, suffix, str_else) \ (cond) ? (prefix) : "", \ (cond) ? (str) : (str_else), \ diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index a077740dc9..2918a9a695 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -4449,6 +4449,94 @@ test_nm_utils_ptrarray_find_binary_search (void) /******************************************************************************/ +static int +_test_nm_in_set_get (int *call_counter, gboolean allow_called, int value) +{ + g_assert (call_counter); + *call_counter += 1; + if (!allow_called) + g_assert_not_reached (); + return value; +} + +static void +_test_nm_in_set_assert (int *call_counter, int expected) +{ + g_assert (call_counter); + g_assert_cmpint (expected, ==, *call_counter); + *call_counter = 0; +} + +static void +test_nm_in_set (void) +{ + int call_counter = 0; + +#define G(x) _test_nm_in_set_get (&call_counter, TRUE, x) +#define N(x) _test_nm_in_set_get (&call_counter, FALSE, x) +#define _ASSERT(expected, expr) \ + G_STMT_START { \ + _test_nm_in_set_assert (&call_counter, 0); \ + g_assert (expr); \ + _test_nm_in_set_assert (&call_counter, (expected)); \ + } G_STMT_END + _ASSERT (1, !NM_IN_SET (-1, G( 1))); + _ASSERT (1, NM_IN_SET (-1, G(-1))); + + _ASSERT (2, !NM_IN_SET (-1, G( 1), G( 2))); + _ASSERT (2, NM_IN_SET (-1, G(-1), G( 2))); + _ASSERT (2, NM_IN_SET (-1, G( 1), G(-1))); + _ASSERT (2, NM_IN_SET (-1, G(-1), G(-1))); + + _ASSERT (3, !NM_IN_SET (-1, G( 1), G( 2), G( 3))); + _ASSERT (3, NM_IN_SET (-1, G(-1), G( 2), G( 3))); + _ASSERT (3, NM_IN_SET (-1, G( 1), G(-1), G( 3))); + _ASSERT (3, NM_IN_SET (-1, G( 1), G( 2), G(-1))); + _ASSERT (3, NM_IN_SET (-1, G( 1), G(-1), G(-1))); + _ASSERT (3, NM_IN_SET (-1, G(-1), G( 2), G(-1))); + _ASSERT (3, NM_IN_SET (-1, G(-1), G(-1), G( 3))); + _ASSERT (3, NM_IN_SET (-1, G(-1), G(-1), G(-1))); + + _ASSERT (4, !NM_IN_SET (-1, G( 1), G( 2), G( 3), G( 4))); + _ASSERT (4, NM_IN_SET (-1, G(-1), G( 2), G( 3), G( 4))); + _ASSERT (4, NM_IN_SET (-1, G( 1), G(-1), G( 3), G( 4))); + _ASSERT (4, NM_IN_SET (-1, G( 1), G( 2), G(-1), G( 4))); + _ASSERT (4, NM_IN_SET (-1, G( 1), G( 2), G( 3), G(-1))); + + _ASSERT (5, NM_IN_SET (-1, G( 1), G( 2), G( 3), G(-1), G( 5))); + + _ASSERT (1, !NM_IN_SET_SC (-1, G( 1))); + _ASSERT (1, NM_IN_SET_SC (-1, G(-1))); + + _ASSERT (2, !NM_IN_SET_SC (-1, G( 1), G( 2))); + _ASSERT (1, NM_IN_SET_SC (-1, G(-1), N( 2))); + _ASSERT (2, NM_IN_SET_SC (-1, G( 1), G(-1))); + _ASSERT (1, NM_IN_SET_SC (-1, G(-1), N(-1))); + + _ASSERT (3, !NM_IN_SET_SC (-1, G( 1), G( 2), G( 3))); + _ASSERT (1, NM_IN_SET_SC (-1, G(-1), N( 2), N( 3))); + _ASSERT (2, NM_IN_SET_SC (-1, G( 1), G(-1), N( 3))); + _ASSERT (3, NM_IN_SET_SC (-1, G( 1), G( 2), G(-1))); + _ASSERT (2, NM_IN_SET_SC (-1, G( 1), G(-1), N(-1))); + _ASSERT (1, NM_IN_SET_SC (-1, G(-1), N( 2), N(-1))); + _ASSERT (1, NM_IN_SET_SC (-1, G(-1), N(-1), N( 3))); + _ASSERT (1, NM_IN_SET_SC (-1, G(-1), N(-1), N(-1))); + + _ASSERT (4, !NM_IN_SET_SC (-1, G( 1), G( 2), G( 3), G( 4))); + _ASSERT (1, NM_IN_SET_SC (-1, G(-1), N( 2), N( 3), N( 4))); + _ASSERT (2, NM_IN_SET_SC (-1, G( 1), G(-1), N( 3), N( 4))); + _ASSERT (3, NM_IN_SET_SC (-1, G( 1), G( 2), G(-1), N( 4))); + _ASSERT (4, NM_IN_SET_SC (-1, G( 1), G( 2), G( 3), G(-1))); + + _ASSERT (4, NM_IN_SET_SC (-1, G( 1), G( 2), G( 3), G(-1), G( 5))); + +#undef G +#undef N +#undef _ASSERT +} + +/******************************************************************************/ + NMTST_DEFINE (); int main (int argc, char **argv) @@ -4456,6 +4544,7 @@ int main (int argc, char **argv) nmtst_init (&argc, &argv, TRUE); /* The tests */ + g_test_add_func ("/core/general/test_nm_in_set", test_nm_in_set); g_test_add_func ("/core/general/test_setting_vpn_items", test_setting_vpn_items); g_test_add_func ("/core/general/test_setting_vpn_update_secrets", test_setting_vpn_update_secrets); g_test_add_func ("/core/general/test_setting_vpn_modify_during_foreach", test_setting_vpn_modify_during_foreach);