Import CStringView from the Babeltrace tree
[lttng-tools.git] / src / common / macros.hpp
... / ...
CommitLineData
1/*
2 * Copyright (C) 2011 EfficiOS Inc.
3 * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-only
6 *
7 */
8
9#ifndef _MACROS_H
10#define _MACROS_H
11
12#include <common/compat/string.hpp>
13
14#include <memory>
15#include <pthread.h>
16#include <stddef.h>
17#include <stdlib.h>
18#include <string.h>
19#include <type_traits>
20
21/*
22 * Takes a pointer x and transform it so we can use it to access members
23 * without a function call. Here an example:
24 *
25 * #define GET_SIZE(x) LTTNG_REF(x)->size
26 *
27 * struct { int size; } s;
28 *
29 * printf("size : %d\n", GET_SIZE(&s));
30 *
31 * For this example we can't use something like this for compatibility purpose
32 * since this will fail:
33 *
34 * #define GET_SIZE(x) x->size;
35 *
36 * This is mostly use for the compatibility layer of lttng-tools. See
37 * poll/epoll for a good example. Since x can be on the stack or allocated
38 * memory using malloc(), we must use generic accessors for compat in order to
39 * *not* use a function to access members and not the variable name.
40 */
41#define LTTNG_REF(x) ((typeof(*(x)) *) (x))
42
43#ifdef NDEBUG
44/*
45 * Force usage of the assertion condition to prevent unused variable warnings
46 * when `assert()` are disabled by the `NDEBUG` definition.
47 */
48#define LTTNG_ASSERT(_cond) ((void) sizeof((void) (_cond), 0))
49#else
50#include <assert.h>
51#define LTTNG_ASSERT(_cond) assert(_cond)
52#endif
53
54/*
55 * Memory allocation zeroed
56 */
57
58static inline void *zmalloc_internal(size_t size)
59{
60 return calloc(1, size);
61}
62
63template <typename MallocableType>
64struct can_malloc {
65 /*
66 * gcc versions before 5.0 lack some type traits defined in C++11.
67 * Since in this instance we use the trait to prevent misuses
68 * of malloc (and statically assert) and not to generate different
69 * code based on this property, simply set value to true and allow
70 * the code to compile. Anyone using a contemporary compiler will
71 * catch the error.
72 */
73#if __GNUG__ && __GNUC__ < 5
74 static constexpr bool value = true;
75#else
76 static constexpr bool value = std::is_trivially_constructible<MallocableType>::value;
77#endif
78};
79
80/*
81 * Malloc and zero-initialize an object of type T, asserting that MallocableType can be
82 * safely malloc-ed (is trivially constructible).
83 */
84template <typename MallocableType>
85MallocableType *zmalloc()
86{
87 static_assert(can_malloc<MallocableType>::value, "type can be malloc'ed");
88 return (MallocableType *) zmalloc_internal(sizeof(MallocableType)); /* NOLINT sizeof
89 potentially used on a
90 pointer. */
91}
92
93/*
94 * Malloc and zero-initialize a buffer of size `size`, asserting that type AllocatedType
95 * can be safely malloc-ed (is trivially constructible).
96 */
97template <typename AllocatedType>
98AllocatedType *zmalloc(size_t size)
99{
100 static_assert(can_malloc<AllocatedType>::value, "type can be malloc'ed");
101 LTTNG_ASSERT(size >= sizeof(AllocatedType));
102 return (AllocatedType *) zmalloc_internal(size);
103}
104
105/*
106 * Malloc and zero-initialize an array of `nmemb` elements of type AllocatedType,
107 * asserting that AllocatedType can be safely malloc-ed (is trivially constructible).
108 */
109template <typename AllocatedType>
110AllocatedType *calloc(size_t nmemb)
111{
112 static_assert(can_malloc<AllocatedType>::value, "type can be malloc'ed");
113 return (AllocatedType *) zmalloc_internal(nmemb * sizeof(AllocatedType)); /* NOLINT sizeof
114 potentially
115 used on a
116 pointer. */
117}
118
119/*
120 * Malloc an object of type AllocatedType, asserting that AllocatedType can be safely malloc-ed (is
121 * trivially constructible).
122 */
123template <typename AllocatedType>
124AllocatedType *malloc()
125{
126 static_assert(can_malloc<AllocatedType>::value, "type can be malloc'ed");
127 return (AllocatedType *) malloc(sizeof(AllocatedType));
128}
129
130/*
131 * Malloc a buffer of size `size`, asserting that AllocatedType can be safely
132 * malloc-ed (is trivially constructible).
133 */
134template <typename AllocatedType>
135AllocatedType *malloc(size_t size)
136{
137 static_assert(can_malloc<AllocatedType>::value, "type can be malloc'ed");
138 return (AllocatedType *) malloc(size);
139}
140
141/*
142 * Prevent using `free` on types that are non-POD.
143 *
144 * Declare a delete prototype of free if the parameter type is not safe to free
145 * (non-POD).
146 *
147 * If the parameter is a pointer to void, we can't check if what is pointed
148 * to is safe to free or not, as we don't know what is pointed to. Ideally,
149 * all calls to free would be with a typed pointer, but there are too many
150 * instances of passing a pointer to void to enforce that right now. So allow
151 * pointers to void, these will not be checked.
152 */
153
154template <typename FreedType>
155struct can_free {
156 /*
157 * gcc versions before 5.0 lack some type traits defined in C++11.
158 * Since in this instance we use the trait to prevent misuses
159 * of free (and statically assert) and not to generate different
160 * code based on this property, simply set value to true and allow
161 * the code to compile. Anyone using a contemporary compiler will
162 * catch the error.
163 */
164#if __GNUG__ && __GNUC__ < 5
165 static constexpr bool value = true;
166#else
167 static constexpr bool value = std::is_trivially_destructible<FreedType>::value ||
168 std::is_void<FreedType>::value;
169#endif
170};
171
172template <typename FreedType, typename = typename std::enable_if<!can_free<FreedType>::value>::type>
173void free(FreedType *p) = delete;
174
175template <typename InitializedType>
176struct can_memset {
177 static constexpr bool value = std::is_pod<InitializedType>::value ||
178 std::is_void<InitializedType>::value;
179};
180
181template <typename InitializedType,
182 typename = typename std::enable_if<!can_memset<InitializedType>::value>::type>
183void *memset(InitializedType *s, int c, size_t n) = delete;
184
185template <typename T>
186struct can_memcpy {
187 /*
188 * gcc versions before 5.0 lack some type traits defined in C++11.
189 * Since in this instance we use the trait to prevent misuses
190 * of memcpy (and statically assert) and not to generate different
191 * code based on this property, simply set value to true and allow
192 * the code to compile. Anyone using a contemporary compiler will
193 * catch the error.
194 */
195#if __GNUG__ && __GNUC__ < 5
196 static constexpr bool value = true;
197#else
198 static constexpr bool value = std::is_trivially_copyable<T>::value;
199#endif
200};
201
202template <typename DestinationType,
203 typename SourceType,
204 typename = typename std::enable_if<!can_memcpy<DestinationType>::value>::type,
205 typename = typename std::enable_if<!can_memcpy<SourceType>::value>::type>
206void *memcpy(DestinationType *d, const SourceType *s, size_t n) = delete;
207
208template <typename MovedType>
209struct can_memmove {
210 /*
211 * gcc versions before 5.0 lack some type traits defined in C++11.
212 * Since in this instance we use the trait to prevent misuses
213 * of memmove (and statically assert) and not to generate different
214 * code based on this property, simply set value to true and allow
215 * the code to compile. Anyone using a contemporary compiler will
216 * catch the error.
217 */
218#if __GNUG__ && __GNUC__ < 5
219 static constexpr bool value = true;
220#else
221 static constexpr bool value = std::is_trivially_copyable<MovedType>::value;
222#endif
223};
224
225template <typename DestinationType,
226 typename SourceType,
227 typename = typename std::enable_if<!can_memmove<DestinationType>::value>::type,
228 typename = typename std::enable_if<!can_memmove<SourceType>::value>::type>
229void *memmove(DestinationType *d, const SourceType *s, size_t n) = delete;
230
231#ifndef ARRAY_SIZE
232#define ARRAY_SIZE(array) (sizeof(array) / (sizeof((array)[0])))
233#endif
234
235#ifndef LTTNG_PACKED
236#define LTTNG_PACKED __attribute__((__packed__))
237#endif
238
239#ifndef LTTNG_NO_SANITIZE_ADDRESS
240#if defined(__clang__) || defined(__GNUC__)
241#define LTTNG_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
242#else
243#define LTTNG_NO_SANITIZE_ADDRESS
244#endif
245#endif
246
247#define member_sizeof(type, field) sizeof(((type *) 0)->field)
248
249#define ASSERT_LOCKED(lock) LTTNG_ASSERT(pthread_mutex_trylock(&(lock)))
250#define ASSERT_RCU_READ_LOCKED() LTTNG_ASSERT(rcu_read_ongoing())
251#define ASSERT_RCU_READ_UNLOCKED() LTTNG_ASSERT(!rcu_read_ongoing())
252
253/* Attribute suitable to tag functions as having printf()-like arguments. */
254#define ATTR_FORMAT_PRINTF(_string_index, _first_to_check) \
255 __attribute__((format(printf, _string_index, _first_to_check)))
256
257/* Attribute suitable to tag functions as having strftime()-like arguments. */
258#define ATTR_FORMAT_STRFTIME(_string_index) __attribute__((format(strftime, _string_index, 0)))
259
260/* Macros used to ignore specific compiler diagnostics. */
261
262#define DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push")
263#define DIAGNOSTIC_POP _Pragma("GCC diagnostic pop")
264
265#if defined(__clang__)
266/* Clang */
267#define DIAGNOSTIC_IGNORE_SUGGEST_ATTRIBUTE_FORMAT
268#define DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL \
269 _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"")
270#define DIAGNOSTIC_IGNORE_LOGICAL_OP
271#define DIAGNOSTIC_IGNORE_DUPLICATED_BRANCHES
272#define DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
273_Pragma("GCC diagnostic ignored \"-Winvalid-offsetof\"")
274#else
275/* GCC */
276#define DIAGNOSTIC_IGNORE_SUGGEST_ATTRIBUTE_FORMAT \
277 _Pragma("GCC diagnostic ignored \"-Wsuggest-attribute=format\"")
278#define DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL \
279 _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"")
280#define DIAGNOSTIC_IGNORE_LOGICAL_OP _Pragma("GCC diagnostic ignored \"-Wlogical-op\"")
281#if __GNUG__ && __GNUC__ >= 7
282#define DIAGNOSTIC_IGNORE_DUPLICATED_BRANCHES \
283 _Pragma("GCC diagnostic ignored \"-Wduplicated-branches\"")
284#else
285#define DIAGNOSTIC_IGNORE_DUPLICATED_BRANCHES
286#endif /* __GNUG__ && __GNUC__ >= 7 */
287#define DIAGNOSTIC_IGNORE_INVALID_OFFSETOF _Pragma("GCC diagnostic ignored \"-Winvalid-offsetof\"")
288#endif
289
290/* Used to make specific C++ functions to C code. */
291#ifdef __cplusplus
292#define C_LINKAGE extern "C"
293#else
294#define C_LINKAGE
295#endif
296
297 /*
298 * lttng_strncpy returns 0 on success, or nonzero on failure.
299 * It checks that the @src string fits into @dst_len before performing
300 * the copy. On failure, no copy has been performed.
301 *
302 * Assumes that 'src' is null-terminated.
303 *
304 * dst_len includes the string's trailing NULL.
305 */
306 static inline int lttng_strncpy(char *dst, const char *src, size_t dst_len)
307{
308 if (strlen(src) >= dst_len) {
309 /* Fail since copying would result in truncation. */
310 return -1;
311 }
312 strcpy(dst, src);
313 return 0;
314}
315
316namespace lttng {
317namespace utils {
318template <class ParentType, class MemberType>
319ParentType *container_of(const MemberType *member, const MemberType ParentType::*ptr_to_member)
320{
321 const ParentType *dummy_parent = nullptr;
322 auto *offset_of_member = reinterpret_cast<const char *>(&(dummy_parent->*ptr_to_member));
323 auto address_of_parent = reinterpret_cast<const char *>(member) - offset_of_member;
324
325 return reinterpret_cast<ParentType *>(address_of_parent);
326}
327} /* namespace utils */
328} /* namespace lttng */
329
330#endif /* _MACROS_H */
This page took 0.023024 seconds and 4 git commands to generate.