Difference between revisions of "Macros"
m (Add condensed notes on DT_HAS_NODE_STATUS()) |
m (General Articles about Macros : add Yale older CS pages link) |
||
| Line 12: | Line 12: | ||
# https://en.wikipedia.org/wiki/C_preprocessor#X-Macros<br /> | # https://en.wikipedia.org/wiki/C_preprocessor#X-Macros<br /> | ||
# https://en.wikipedia.org/wiki/X_macro<br /> | # https://en.wikipedia.org/wiki/X_macro<br /> | ||
| + | # https://www.cs.yale.edu/homes/aspnes/pinewiki/C(2f)Macros.html | ||
=== [[#top|^]] do while(0) === | === [[#top|^]] do while(0) === | ||
Latest revision as of 15:03, 2 June 2026
Macros - C Preprocessor Macro language
C's preprocessor language is an important feature and tool in the development space of C language projects. This local wiki page captures some references to preprocessor macro language.
^ General Articles about Macros
Some general and comprehensive discussions of C macros can be found at:
- https://home.cs.colorado.edu/~main/cs1300/doc/gnu/cpp_1.html
- https://stackoverflow.com/questions/4674480/do-whilefalse-pattern
- https://www.mikeash.com/pyblog/friday-qa-2010-12-31-c-macro-tips-and-tricks.html
- https://en.wikipedia.org/wiki/C_preprocessor#X-Macros
- https://en.wikipedia.org/wiki/X_macro
- https://www.cs.yale.edu/homes/aspnes/pinewiki/C(2f)Macros.html
^ do while(0)
Articles on the use of C language 'do while(0)' construct in macros. The do while(0) construct is the only C syntax which expands correctly from C macros regardless of the use of curly braces and semicolons around the given macro.
^ stringification macro pair
A useful macro pair, and this must be a macro pair, is the following set of defines. These defines will accept a string and put double quotes around it:
#define TO_QUOTE_STRING(string) #string #define WRAPPER_TO_QUOTE_STRING(token) TO_QUOTE_STRING(string)
Guy Rutenberg explains this C pre-processor pattern:
The core rule at play is that C preprocessor does not expand macros preceded by the `#` preprocessor directive, the directive to quote a token. By creating a wrapping macro which does not call the `#` directive, the argument to the wrapper is expanded and then passed to the quoting directive.
^ fallthrough macro
The `__fallthrough()` macro appears to be a C++ macro. It may be found among Zephyr RTOS 3.4.0 source tree contents. An article on the concept of "fall through" in C switch statements:
^ X macro idiom
X macros in C, a pattern involving nested macros makes use of nesting an initially declared and not defined macro within another macro. Danilafe and Phillip Trudeau offer the following blog posts on this topic:
Macro tips and idioms:
^ FOREACH macro implementations (may not be fully possible)
Macro examples from Zephyr RTOS release 3.7.1:
``` ./include/zephyr/devicetree.h:4527:#define DT_INST_FOREACH_STATUS_OKAY(fn) \ ./include/zephyr/devicetree.h:4544:#define DT_INST_FOREACH_STATUS_OKAY_VARGS(fn, ...) \ ```
Related to Zephyr RTOS sys util macros and Zephyr's _COND_CODE_1 and _COND_CODE_0 macros:
Zephyr device tree macros include a macro to conditionally compile code based on a device tree node property:
From devicetree.h:
3384 #define DT_NODE_HAS_STATUS(node_id, status) \
3385 DT_NODE_HAS_STATUS_INTERNAL(node_id, status)
4805 /** @brief Helper for DT_NODE_HAS_STATUS */i
4806 #define DT_NODE_HAS_STATUS_INTERNAL(node_id, status) \
4807 IS_ENABLED(DT_CAT3(node_id, _STATUS_, status))
./include/zephyr/sys/util_macro.h:124:#define IS_ENABLED(config_macro) Z_IS_ENABLED1(config_macro)
96 /**
97 * @brief Check for macro definition in compiler-visible expressions
98 *
99 * This trick was pioneered in Linux as the config_enabled() macro. It
100 * has the effect of taking a macro value that may be defined to "1"
101 * or may not be defined at all and turning it into a literal
102 * expression that can be handled by the C compiler instead of just
103 * the preprocessor. It is often used with a @p CONFIG_FOO macro which
104 * may be defined to 1 via Kconfig, or left undefined.
105 *
106 * That is, it works similarly to <tt>\#if defined(CONFIG_FOO)</tt>
107 * except that its expansion is a C expression. Thus, much <tt>\#ifdef</tt>
108 * usage can be replaced with equivalents like:
109 *
110 * if (IS_ENABLED(CONFIG_FOO)) {
111 * do_something_with_foo
112 * }
113 *
114 * This is cleaner since the compiler can generate errors and warnings
115 * for @p do_something_with_foo even when @p CONFIG_FOO is undefined.
116 *
117 * Note: Use of IS_ENABLED in a <tt>\#if</tt> statement is discouraged
118 * as it doesn't provide any benefit vs plain <tt>\#if defined()</tt>
119 *
120 * @param config_macro Macro to check
121 * @return 1 if @p config_macro is defined to 1, 0 otherwise (including
122 * if @p config_macro is not defined)
123 */
124 #define IS_ENABLED(config_macro) Z_IS_ENABLED1(config_macro)
125 /* INTERNAL: the first pass above is just to expand any existing
126 * macros, we need the macro value to be e.g. a literal "1" at
127 * expansion time in the next macro, not "(1)", etc... Standard
128 * recursive expansion does not work.
129 */
./sys/util_internal.h:28:#define Z_IS_ENABLED1(config_macro) Z_IS_ENABLED2(_XXXX##config_macro)
Zephyr's Z_IS_ENABLED() macro has three helpers defined in sys/util_internal.h:
20 /* IS_ENABLED() helpers */ 21 22 /* This is called from IS_ENABLED(), and sticks on a "_XXXX" prefix, 23 * it will now be "_XXXX1" if config_macro is "1", or just "_XXXX" if it's 24 * undefined. 25 * ENABLED: Z_IS_ENABLED2(_XXXX1) 26 * DISABLED Z_IS_ENABLED2(_XXXX) 27 */ 28 #define Z_IS_ENABLED1(config_macro) Z_IS_ENABLED2(_XXXX##config_macro) 29 30 /* Here's the core trick, we map "_XXXX1" to "_YYYY," (i.e. a string 31 * with a trailing comma), so it has the effect of making this a 32 * two-argument tuple to the preprocessor only in the case where the 33 * value is defined to "1" 34 * ENABLED: _YYYY, <--- note comma! 35 * DISABLED: _XXXX 36 */ 37 #define _XXXX1 _YYYY, 38 39 /* Then we append an extra argument to fool the gcc preprocessor into 40 * accepting it as a varargs macro. 41 * arg1 arg2 arg3 42 * ENABLED: Z_IS_ENABLED3(_YYYY, 1, 0) 43 * DISABLED Z_IS_ENABLED3(_XXXX 1, 0) 44 */ 45 #define Z_IS_ENABLED2(one_or_two_args) Z_IS_ENABLED3(one_or_two_args 1, 0) 46 47 /* And our second argument is thus now cooked to be 1 in the case 48 * where the value is defined to 1, and 0 if not: 49 */ 50 #define Z_IS_ENABLED3(ignore_this, val, ...) val
^ CONTAINER_OF macro
Helpful blogpost by Radek Pazdera, full-stack software engineer based in London . . .
See also 2026 Q1 local page on developers.
^ To use GCC to see macro expansion
Here is an example shell script which invokes gcc on a single C source file, and causes only the preprocessor to execute. This results in a display of all preprocessor macros:
#!/bin/bash # References: # * https://gcc.gnu.org/onlinedocs/gcc-3.2.2/cpp/Search-Path.html # $workspace/zephyr/subsys/testsuite/include/zephyr/fff.h gcc -E \ -I../../../../../zephyr/subsys/testsuite/include \ -I../../../../../zephyr/include \ ./unit-test-mocked-functions.h exit $?