Loading...
Searching...
No Matches
util_macros.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2024-2026 Carl Seifert
3 * SPDX-FileCopyrightText: 2024-2026 TU Dresden
4 * SPDX-License-Identifier: LGPL-2.1-only
5 */
6
7#pragma once
8
9#include "modules.h"
10#include "macros/utils.h"
11
18
19#ifndef DOXYGEN
20# define UNICOAP_CODE_CLASS_DETAIL_FORMAT "%u.%02u"
21
22# define _CONCAT2(a, b) a ## b
23
24# define _CONCAT3(a, b, c) CONCAT3(a, b, c)
25
26/* This is best explained by the following two examples.
27 * --> denotes a proprocessor evaluation step.
28 *
29 * __unicoap_bit(11)
30 * --> __unicoap_create_bit_or_nothing(11, CONCAT(__unicoap_two_empty_args_, 11))
31 * --> __unicoap_create_bit_or_nothing(11, __unicoap_two_empty_args_11)
32 * \...... __VA_ARGS__ ......./
33 * --> __take_second_arg(__unicoap_two_empty_args_11, (1 << (11)) |)
34 * \...... __VA_ARGS__ ......./
35 * --> (1 << (11)) |
36 *
37 *
38 * __unicoap_bit()
39 * --> __unicoap_create_bit_or_nothing(, CONCAT(__unicoap_two_empty_args_,))
40 * --> __unicoap_create_bit_or_nothing(, __unicoap_two_empty_args_)
41 \.. This is not a token ../
42 as above but a real
43 preprocessor constant.
44
45 * --> __unicoap_create_bit_or_nothing(,~,)
46 \....... Three arguments: empty, tilde, empty
47 * --> __take_second_arg(~,,)
48 * \...... __VA_ARGS__ followed by empty, former first ("offset"), argument
49 * -->
50 * \.... nothing/empty
51 *
52 *
53 * The last trailing OR slash is always followed by a zero:
54 *
55 * UNICOAP_BITFIELD(...) __unicoap_create_bitfield(__VA_ARGS__,,,,,,,,,,,,,,,) 0
56 *
57 * The numerous commas ensure there are always at least 16 arguments, although they may be empty.
58 * See the explainer above how an expansion of __unicoap_bit works with an empty argument.
59 */
60
61# define __unicoap_create_bit_or_nothing(offset, ...) \
62 __take_second_arg(__VA_ARGS__, (1 << (offset)) |)
63
64# define __unicoap_two_empty_args_ ~,
65
66# define __unicoap_bit(offset, ...) \
67 __unicoap_create_bit_or_nothing(offset, _CONCAT2(__unicoap_two_empty_args_, offset))
68
69# define __unicoap_create_bitfield(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, \
70 _15, _16, ...) \
71 __unicoap_bit(_1) __unicoap_bit(_2) __unicoap_bit(_3) __unicoap_bit(_4) __unicoap_bit(_5) \
72 __unicoap_bit(_6) __unicoap_bit(_7) __unicoap_bit(_8) __unicoap_bit(_9) \
73 __unicoap_bit(_10) __unicoap_bit(_11) __unicoap_bit(_12) __unicoap_bit(_13) \
74 __unicoap_bit(_14) __unicoap_bit(_15) __unicoap_bit(_16)
75#endif
76
83#define UNICOAP_BITFIELD(...) __unicoap_create_bitfield(__VA_ARGS__,,,,,,,,,,,,,,,) 0
84
85/* _Generic can also be available as a language extension before C11, so we check those first */
86#ifndef DOXYGEN
87/* clang */
88# if defined(__has_extension)
89# if __has_extension(c_generic_selection_with_controlling_type)
90# define _UNICOAP_GENERIC_CONTROLLING_TYPE_AVAILABLE 1
91# endif
92# endif
93
94/* GCC */
95# if !defined(_UNICOAP_GENERIC_CONTROLLING_TYPE_AVAILABLE) && defined(__GNUC__) \
96 && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9))
97# define _UNICOAP_GENERIC_CONTROLLING_TYPE_AVAILABLE 1
98# endif
99
100/* C11 */
101# if !defined(_UNICOAP_GENERIC_CONTROLLING_TYPE_AVAILABLE) && __STDC_VERSION__ >= 201112L
102# define _UNICOAP_GENERIC_CONTROLLING_TYPE_AVAILABLE 1
103# endif
104#endif
105
106/* unicoap_job_t wraps event_t so that changing the internal event loop to something else later
107 * won't induce a source-breaking change. Hence, an initializer is required to hide that complexity.
108 * To allow compile-time initialization, we use a UNICOAP_JOB macro. A static inline function
109 * won't work as such a function won't be able to produce a constant expression.
110 *
111 * Because unicoap_job_t uses an event_t internally, the event callback must be casted. Since
112 * the only argument is a pointer and the return type is void, that cast can be considered safe.
113 * The problem that arises is that deviant function pointers will silently be casted to the
114 * same function prototype. Since we cannot use a static inline function to typecheck the
115 * function pointer, we had to come up with a macro that typechecks the argument to detect
116 * mismatches in the function pointer type. This is what the following macro does.
117 *
118 * It uses _Generic as a compile-time switch expression to verify that the given argument really
119 * matches the expected type.
120 *
121 * We may, in the future, also allow void (*)(event_t*) as job function type, which is as simple
122 * as adding another controlling type case to _Generic in _UNICOAP_TRY_TYPECHECK_JOB_FUNC.
123 */
124#ifndef DOXYGEN
125# if defined(_UNICOAP_GENERIC_CONTROLLING_TYPE_AVAILABLE)
126# define _UNICOAP_TRY_TYPECHECK_JOB_FUNC(func) _Generic((func), \
127 void (*)(unicoap_job_t*): (void (*)(event_t*))func \
128 )
129# else
130# define _UNICOAP_TRY_TYPECHECK_JOB_FUNC(func) (void (*)(event_t*))func
131# endif
132#endif
133
134
135#ifndef DOXYGEN
136/* This macro can cause a static assertion failure from within an expression without an additional
137 * statement. It encapsulates the static_assert in a sizeof(struct { ... }) expression, and it
138 * discards that value by multiplying with 0. */
139# define __unicoap_diagnose(condition, message) \
140 (0 * sizeof(struct { static_assert(condition, message); int dummy; }))
141#endif
142
143#ifndef DOXYGEN
144/* Here we check every every path component passed to UNICOAP_PATH for its type and if it's NULL
145 * at compile time. We use _Generic as that built-in maps NULL to void*, which is a type we can
146 * just not support as the controlling type in _Generic. We do however support char* and const char*.
147 */
148/* The placeholder type used as to fill the list of components to 16 */
149struct __unicoap_placeholder_type { int _dummy; };
150# define __UNICOAP_PLACEHOLDER (struct __unicoap_placeholder_type){ 0 }
151
153# define __unicoap_or_16( \
154 _f, \
155 _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, \
156 ... \
157 ) \
158 _f(_1) | _f(_2) | _f(_3) | _f(_4) | _f(_5) | _f(_6) | _f(_7) | _f(_8) | _f(_9) | _f(_10) | \
159 _f(_11) | _f(_12) | _f(_13) | _f(_14) | _f(_15) | _f(_16)
160
161/* Why do we produce an OR sequence of zeroes? We could also produce a list of discarded values,
162 * but discarding values in round brackets (discard, discard, ..., real) does not produce
163 * a constant (compile-time) expression. So instead, we produce 0 | 0 | ... | 0,
164 * and then call _UNICOAP_TRY_CHECK_PATH_COMPONENTS(__VA_ARGS__) ? V : V at the call site,
165 * where we discard the result (zero) by using the same value V in both branches of the ternary
166 * expression. This way we 'use' that (zero) value in the eyes of the compiler, but also
167 * disregard it. After all, we only care that _UNICOAP_TRY_CHECK_PATH_COMPONENTS produces an
168 * an expression that can be compiled. The value does not matter -- hence 0 in every branch below.
169 */
170
171# define __UNICOAP_CHECK_PATH_COMPONENT_NONEMTPY(x) \
172 __unicoap_diagnose(sizeof(x) > 1, "Empty string literals as path components are disallowed.")
173
174# if defined(_UNICOAP_GENERIC_CONTROLLING_TYPE_AVAILABLE)
175# define __UNICOAP_CHECK_PATH_COMPONENT(x) _Generic((x), \
176 char *: 0, \
177 const char *: 0, \
178 struct __unicoap_placeholder_type: 0 \
179 ) | __UNICOAP_CHECK_PATH_COMPONENT_NONEMTPY(x)
180# else
181# define __UNICOAP_CHECK_PATH_COMPONENT(x) __UNICOAP_CHECK_PATH_COMPONENT_NONEMTPY(x)
182# endif
183
184# define _UNICOAP_TRY_CHECK_PATH_COMPONENTS(...) \
185 (__unicoap_or_16( \
186 __UNICOAP_CHECK_PATH_COMPONENT, __VA_ARGS__, \
187 __UNICOAP_PLACEHOLDER, __UNICOAP_PLACEHOLDER, __UNICOAP_PLACEHOLDER, __UNICOAP_PLACEHOLDER,\
188 __UNICOAP_PLACEHOLDER, __UNICOAP_PLACEHOLDER, __UNICOAP_PLACEHOLDER, __UNICOAP_PLACEHOLDER,\
189 __UNICOAP_PLACEHOLDER, __UNICOAP_PLACEHOLDER, __UNICOAP_PLACEHOLDER, __UNICOAP_PLACEHOLDER,\
190 __UNICOAP_PLACEHOLDER, __UNICOAP_PLACEHOLDER, __UNICOAP_PLACEHOLDER, __UNICOAP_PLACEHOLDER \
191 ))
192#endif
193
194#ifdef __cplusplus
195extern "C" {}
196#endif
Various helper macros.
Common macros and compiler attributes/pragmas configuration.