From ef4e629502eeb56f59d780ec9244864d3040e88e Mon Sep 17 00:00:00 2001 From: Daisuke Nojiri Date: Fri, 24 May 2019 10:10:10 -0700 Subject: common: Make IS_ENABLED fail on unknown values Partial cherry-pick of CL:1592728. Signed-off-by: Daisuke Nojiri BUG=none BRANCH=none TEST=make BOARD=nami Change-Id: I8c057989bbaf006f06c803ca107d904fdeb22cca Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1626188 Reviewed-by: Daisuke Nojiri Commit-Queue: Daisuke Nojiri Tested-by: Daisuke Nojiri --- include/common.h | 52 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/include/common.h b/include/common.h index c7a2d94460..bc1da44433 100644 --- a/include/common.h +++ b/include/common.h @@ -220,29 +220,49 @@ enum ec_error_list { /* * Getting something that works in C and CPP for an arg that may or may - * not be defined is tricky. Here, if we have "#define CONFIG_BOOGER" - * we match on the placeholder define, insert the "0," for arg1 and generate - * the triplet (0, 1, 0). Then the last step cherry picks the 2nd arg (a one). - * When CONFIG_BOOGER is not defined, we generate a (... 1, 0) pair, and when - * the last step cherry picks the 2nd arg, we get a zero. + * not be defined is tricky. Here, if we have "#define CONFIG_FOO" + * we match on the placeholder define, insert the "_, 1," for arg1 and generate + * the triplet (_, 1, _, (...)). Then the last step cherry picks the 2nd arg + * (a one). + * When CONFIG_FOO is not defined, we generate a (_, (...)) pair, and when + * the last step cherry picks the 2nd arg, we get a code block that verifies + * the value of the option. Since the preprocessor won't replace an unknown + * token, we compare the option name with the value string. If they are + * identical we assume that the value was undefined and return 0. If the value + * happens to be anything else we call an undefined method that will raise + * a compiler error. This technique requires that the optimizer be enabled so it + * can remove the undefined function call. + * */ -#define __ARG_PLACEHOLDER_ 0, -#define config_enabled(cfg) _config_enabled(cfg) -#define _config_enabled(value) __config_enabled(__ARG_PLACEHOLDER_##value) -#define __config_enabled(arg1_or_junk) ___config_enabled(arg1_or_junk 1, 0, 0) +#define __ARG_PLACEHOLDER_ _, 1, +#define _config_enabled(cfg, value) \ + __config_enabled(__ARG_PLACEHOLDER_##value, cfg, value) +#define __config_enabled(arg1_or_junk, cfg, value) ___config_enabled( \ + arg1_or_junk _,\ + ({ \ + int __undefined = __builtin_strcmp(cfg, #value) == 0; \ + extern int IS_ENABLED_BAD_ARGS(void) __attribute__(( \ + error(cfg " must be , or not defined.")));\ + if (!__undefined) \ + IS_ENABLED_BAD_ARGS(); \ + 0; \ + })) #define ___config_enabled(__ignored, val, ...) val /** - * Checks if a config option is defined to an empty value. + * Checks if a config option is enabled or disabled + * + * Enabled examples: + * #define CONFIG_FOO * - * IS_ENABLED(CONFIG_MY_OPTION) will return 1 in the following case: - * #define CONFIG_MY_OPTION + * Disabled examples: + * #undef CONFIG_FOO * - * Otherwise if the option has not been defined or defined with a value, it will - * return 0. + * If the option is defined to any value a compiler error will be thrown. * - * @param CONFIG_OPTION + * Note: This macro will only function inside a code block due to the way + * it checks for unknown values. */ -#define IS_ENABLED(option) config_enabled(option) +#define IS_ENABLED(option) _config_enabled(#option, option) #endif /* __CROS_EC_COMMON_H */ -- cgit v1.2.1