Skip to content

Commit e3144ca

Browse files
npitrenashif
authored andcommitted
sys: util: improve IS_EMPTY() implementation
The current implementation relies on preprocessor concatenation to work. This makes it incompatible with any content which expansion is not a valid preprocessor token such as strings, pointers, etc. and therefore limits its usefulness. Replace it with an implementation that can cope with all cases. Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
1 parent e710f5a commit e3144ca

3 files changed

Lines changed: 23 additions & 14 deletions

File tree

include/sys/util_internal.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,23 @@
6767
#define __DEBRACKET(...) __VA_ARGS__
6868

6969
/* Used by IS_EMPTY() */
70-
#define Z_IS_EMPTY_(...) Z_IS_EMPTY__(__VA_ARGS__)
71-
#define Z_IS_EMPTY__(a, ...) Z_IS_EMPTY___(_ZZ##a##ZZ0, __VA_ARGS__)
72-
#define Z_IS_EMPTY___(...) GET_ARG_N(3, __VA_ARGS__)
70+
/* reference: https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/ */
71+
#define Z_HAS_COMMA(...) \
72+
NUM_VA_ARGS_LESS_1_IMPL(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, \
73+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
74+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
75+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
76+
#define Z_TRIGGER_PARENTHESIS_(...) ,
77+
#define Z_IS_EMPTY_(...) \
78+
Z_IS_EMPTY__( \
79+
Z_HAS_COMMA(__VA_ARGS__), \
80+
Z_HAS_COMMA(Z_TRIGGER_PARENTHESIS_ __VA_ARGS__), \
81+
Z_HAS_COMMA(__VA_ARGS__ (/*empty*/)), \
82+
Z_HAS_COMMA(Z_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/)))
83+
#define Z_CAT5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
84+
#define Z_IS_EMPTY__(_0, _1, _2, _3) \
85+
Z_HAS_COMMA(Z_CAT5(Z_IS_EMPTY_CASE_, _0, _1, _2, _3))
86+
#define Z_IS_EMPTY_CASE_0001 ,
7387

7488
/* Used by LIST_DROP_EMPTY() */
7589
/* Adding ',' after each element would add empty element at the end of

include/sys/util_macro.h

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -210,15 +210,6 @@ extern "C" {
210210
* This macro may be used with COND_CODE_1() and COND_CODE_0() while
211211
* processing <tt>__VA_ARGS__</tt> to avoid processing empty arguments.
212212
*
213-
* Note that this macro is intended to check macro names that evaluate
214-
* to replacement lists being empty or containing numbers or macro name
215-
* like tokens.
216-
*
217-
* @note Not all arguments are accepted by this macro and compilation will fail
218-
* if argument cannot be concatenated with literal constant. That will
219-
* happen if argument does not start with letter or number. Example
220-
* arguments that will fail during compilation: .arg, (arg), "arg", {arg}.
221-
*
222213
* Example:
223214
*
224215
* #define EMPTY
@@ -234,9 +225,9 @@ extern "C" {
234225
* In above examples, the invocations of IS_EMPTY(...) return @p true,
235226
* @p false, and @p true; @p some_conditional_code is included.
236227
*
237-
* @param a macro to check for emptiness
228+
* @param ... macro to check for emptiness (may be <tt>__VA_ARGS__</tt>)
238229
*/
239-
#define IS_EMPTY(a) Z_IS_EMPTY_(a, 1, 0,)
230+
#define IS_EMPTY(...) Z_IS_EMPTY_(__VA_ARGS__)
240231

241232
/**
242233
* @brief Remove empty arguments from list.

tests/unit/util/test.inc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,10 @@ static void test_IS_EMPTY(void)
354354
"Expected to be empty");
355355
zassert_false(IS_EMPTY(test_IS_EMPTY_NOT_EMPTY),
356356
"Expected to be non-empty");
357+
zassert_false(IS_EMPTY("string"),
358+
"Expected to be non-empty");
359+
zassert_false(IS_EMPTY(&test_IS_EMPTY),
360+
"Expected to be non-empty");
357361
}
358362

359363
static void test_LIST_DROP_EMPTY(void)

0 commit comments

Comments
 (0)