Toggle navigation
Documentation
The friendly Operating System for the Internet of Things
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 */
149
struct
__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
195
extern
"C"
{}
196
#endif
utils.h
Various helper macros.
modules.h
Common macros and compiler attributes/pragmas configuration.
Generated on Sat Jul 25 2026 20:51:22 by
1.13.2