Difference between revisions of "Macros"

From Wiki at Neela Nurseries
Jump to navigation Jump to search
m (Add link to Jens Gustedt Wordpress article on Empty Macro Detection)
m (Add notes on Zephyr DT_NODE_HAS_STATUS() macro)
Line 66: Line 66:
  
 
*  [https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/ Jens Gustedt Wordpress article Empty Macro Detection]
 
*  [https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/ Jens Gustedt Wordpress article Empty Macro Detection]
 +
 +
Zephyr device tree macros include a macro to conditionally compile code based on a device tree node property:
 +
 +
From devicetree.h:
 +
 +
<pre>
 +
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  */
 +
 +
</pre>
  
 
=== [[#top|^]] CONTAINER_OF macro ===
 
=== [[#top|^]] CONTAINER_OF macro ===

Revision as of 18:10, 1 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:

  1. https://home.cs.colorado.edu/~main/cs1300/doc/gnu/cpp_1.html
  2. https://stackoverflow.com/questions/4674480/do-whilefalse-pattern
  3. https://www.mikeash.com/pyblog/friday-qa-2010-12-31-c-macro-tips-and-tricks.html
  4. https://en.wikipedia.org/wiki/C_preprocessor#X-Macros
  5. https://en.wikipedia.org/wiki/X_macro

^ 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  */

^ 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 $?