VLink 2.0.0
A high-performance communication middleware
Loading...
Searching...
No Matches
traits.h
Go to the documentation of this file.
1/*
2 * Copyright (C) 2026 by Thun Lu. All rights reserved.
3 * Author: Thun Lu <thun.lu@zohomail.cn>
4 * Repo: https://github.com/thun-res/vlink
5 * _ __ __ _ __
6 * | | / / / / (_) ____ / /__
7 * | | / / / / / / / __ \ / //_/
8 * | |/ / / /___ / / / / / / / ,<
9 * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 */
23
24/**
25 * @file traits.h
26 * @brief Compile-time type-trait utilities used internally by VLink.
27 *
28 * @details
29 * The @c vlink::Traits namespace collects a set of small, self-contained
30 * template meta-programming helpers that are used throughout the VLink codebase
31 * to detect type properties at compile time. They follow the same conventions
32 * as the C++ standard library @c <type_traits> header.
33 *
34 * All helpers are either:
35 * - Struct templates inheriting from @c std::true_type / @c std::false_type, or
36 * - @c constexpr functions returning @c bool.
37 *
38 * | Helper | Description |
39 * | ------------------- | --------------------------------------------------------------------- |
40 * | EmptyType | An empty tag type used as a placeholder |
41 * | ExpectFalse | Always evaluates to std::false_type (useful in static_assert) |
42 * | Callable | Detects whether T is callable with no arguments |
43 * | Assignable | Detects whether OT is assignable from T |
44 * | EqualityComparable | Detects whether OT supports == with T |
45 * | GreaterComparable | Detects whether OT supports < and > with T |
46 * | Operatorable | Detects whether OT supports << and >> stream operators with T |
47 * | IsAtomic | Detects whether T is a std::atomic specialization |
48 * | IsSharedPtr | Detects whether T is a std::shared_ptr specialization |
49 * | RemoveSharedPtr | Strips the std::shared_ptr wrapper to obtain the element type |
50 * | has_member | Runtime-style SFINAE check for a named member |
51 * | is_non_char_ptr | True when T decays to a non-char pointer |
52 * | is_integer | True for integer types excluding bool, char, and signed/unsigned char |
53 * | is_floating | True for floating-point types |
54 *
55 * @note
56 * These utilities live in the @c vlink::Traits namespace (note the capital T).
57 * @c VLINK_HAS_MEMBER is the corresponding macro wrapper for has_member.
58 *
59 * @par Example: checking for a member at compile time
60 * @code
61 * struct Foo { void bar() {} };
62 * struct Baz {};
63 *
64 * // Using the macro (preferred for member name checks):
65 * static_assert(VLINK_HAS_MEMBER(Foo, bar));
66 * static_assert(!VLINK_HAS_MEMBER(Baz, bar));
67 *
68 * // Using the template directly:
69 * static_assert(vlink::Traits::Callable<std::function<void()>>::value);
70 * @endcode
71 */
72
73#pragma once
74
75#include <atomic>
76#include <memory>
77#include <type_traits>
78
79namespace vlink {
80
81/**
82 * @namespace vlink::Traits
83 * @brief Collection of compile-time type-trait helpers for VLink.
84 */
85namespace Traits { // NOLINT(readability-identifier-naming)
86
87/**
88 * @struct EmptyType
89 * @brief An empty tag type used as a neutral placeholder in template meta-programming.
90 *
91 * @details
92 * Used in places where a type parameter must be provided but carries no data,
93 * for example as the default detail type in the Logger.
94 */
95struct EmptyType {};
96
97/**
98 * @struct ExpectFalse
99 * @brief A type trait that always evaluates to @c std::false_type.
100 *
101 * @details
102 * Useful inside @c static_assert to produce a dependent false, which
103 * prevents the compiler from instantiating the primary template when
104 * a specialization was expected.
105 *
106 * @tparam T Any type (the value is always false regardless of T).
107 */
108template <typename T>
109struct ExpectFalse : std::false_type {};
110
111/**
112 * @struct Callable
113 * @brief Detects whether type @p T is callable with no arguments.
114 *
115 * @details
116 * Inherits from @c std::true_type when @c T can be called as @c t() (i.e.,
117 * a function pointer, lambda, functor, or @c std::function).
118 *
119 * @tparam T The type to test.
120 */
121template <typename T, typename = void>
122struct Callable : std::false_type {};
123
124template <typename T>
125struct Callable<T, std::void_t<decltype(std::declval<T>()())>> : std::true_type {};
126
127/**
128 * @struct Assignable
129 * @brief Detects whether an object of type @p OT can be assigned a value of type @p T.
130 *
131 * @details
132 * Inherits from @c std::true_type when the expression @c ot = t is well-formed.
133 *
134 * @tparam OT The target (left-hand side) type.
135 * @tparam T The source (right-hand side) type.
136 */
137template <typename OT, typename T, typename = void>
138struct Assignable : std::false_type {};
139
140template <typename OT, typename T>
141struct Assignable<OT, T, std::void_t<decltype(std::declval<OT&>() = std::declval<T>())>> : std::true_type {};
142
143/**
144 * @struct EqualityComparable
145 * @brief Detects whether @p OT supports the @c == operator with @p T.
146 *
147 * @details
148 * Inherits from @c std::true_type when @c ot == t is a well-formed expression.
149 *
150 * @tparam OT The left-hand side type.
151 * @tparam T The right-hand side type.
152 */
153template <typename OT, typename T, typename = void>
154struct EqualityComparable : std::false_type {};
155
156template <typename OT, typename T>
157struct EqualityComparable<OT, T, std::void_t<decltype(std::declval<OT>() == std::declval<T>())>> : std::true_type {};
158
159/**
160 * @struct GreaterComparable
161 * @brief Detects whether @p OT supports both @c < and @c > operators with @p T.
162 *
163 * @details
164 * Inherits from @c std::true_type when both @c ot < t and @c ot > t are
165 * well-formed expressions.
166 *
167 * @tparam OT The left-hand side type.
168 * @tparam T The right-hand side type.
169 */
170template <typename OT, typename T, typename = void>
172
173template <typename OT, typename T>
175 OT, T,
176 std::void_t<decltype(std::declval<OT&>() < std::declval<T>()), decltype(std::declval<OT&>() > std::declval<T&>())>>
177 : std::true_type {};
178
179/**
180 * @struct Operatorable
181 * @brief Detects whether @p OT supports @c << and @c >> stream operators with @p T.
182 *
183 * @details
184 * Inherits from @c std::true_type when both @c ot << t and @c ot >> t are
185 * well-formed expressions. Primarily used to detect stream-compatible types.
186 *
187 * @tparam OT The stream type.
188 * @tparam T The value type.
189 */
190template <typename OT, typename T, typename = void>
191struct Operatorable : ExpectFalse<OT> {};
192
193template <typename OT, typename T>
194struct Operatorable<OT, T,
195 std::void_t<decltype(std::declval<OT&>() << std::declval<T>()),
196 decltype(std::declval<OT&>() >> std::declval<T&>())>> : std::true_type {};
197
198/**
199 * @struct IsAtomic
200 * @brief Detects whether type @p T is a @c std::atomic specialization.
201 *
202 * @details
203 * Inherits from @c std::true_type only for @c std::atomic<U> for any @c U.
204 *
205 * @tparam T The type to test.
206 */
207template <typename T>
208struct IsAtomic : std::false_type {};
209
210template <typename T>
211struct IsAtomic<std::atomic<T>> : std::true_type {};
212
213/**
214 * @struct IsSharedPtr
215 * @brief Detects whether type @p T is a @c std::shared_ptr specialization.
216 *
217 * @details
218 * Inherits from @c std::true_type when @p T is (or derives from)
219 * @c std::shared_ptr<element_type>.
220 *
221 * @tparam T The type to test.
222 */
223template <typename T, typename = void>
224struct IsSharedPtr : std::false_type {};
225
226template <typename T>
227struct IsSharedPtr<T, std::void_t<typename T::element_type>>
228 : std::is_base_of<std::shared_ptr<typename T::element_type>, T> {};
229
230/**
231 * @struct RemoveSharedPtr
232 * @brief Strips @c std::shared_ptr to obtain the underlying element type.
233 *
234 * @details
235 * When @p T is a @c std::shared_ptr, @c RemoveSharedPtr<T>::Type is the
236 * contained element type. Otherwise @c Type is @p T itself.
237 *
238 * @tparam T The type from which to remove the shared_ptr wrapper.
239 */
240template <typename T, bool = IsSharedPtr<T>::value>
242 using Type = T; /**< The type without std::shared_ptr. */
243};
244
245template <typename T>
246struct RemoveSharedPtr<T, true> {
247 using Type = typename T::element_type; /**< The type without std::shared_ptr. */
248};
249
250/**
251 * @brief Checks whether type @p T has a member expression accessible via @p f.
252 *
253 * @details
254 * This function is used as a building block for @c VLINK_HAS_MEMBER. It returns
255 * @c true when invoking the lambda @p f with a default-constructed @c T succeeds
256 * (i.e., the member is accessible at compile time).
257 *
258 * @tparam T The type to probe.
259 * @tparam F A callable that takes a @c T and accesses the member of interest.
260 * @param f A lambda of the form @c [](auto&& obj) -> decltype(obj.member) { return 0; }
261 * @return @c true if the member is accessible, @c false otherwise.
262 *
263 * @note Prefer the @c VLINK_HAS_MEMBER macro for member-name checks.
264 */
265template <typename T, typename F>
266[[nodiscard]] static constexpr auto has_member(F&& f) noexcept -> decltype(f(std::declval<T>()), true) {
267 return true;
268}
269
270template <typename>
271// NOLINTNEXTLINE(modernize-avoid-variadic-functions)
272[[nodiscard]] static constexpr auto has_member(...) noexcept {
273 return false;
274}
275
276/**
277 * @brief Returns @c true when @p T decays to a pointer type other than @c char*.
278 *
279 * @details
280 * Used inside VLink serialisation to distinguish raw data pointers from C-strings.
281 *
282 * @tparam T The type to test.
283 * @return @c true for non-char pointer types.
284 */
285template <typename T>
286[[nodiscard]] static constexpr auto is_non_char_ptr() noexcept {
287 return std::is_pointer_v<std::decay_t<T>> &&
288 !std::is_same_v<std::remove_cv_t<std::remove_pointer_t<std::decay_t<T>>>, char>;
289}
290
291/**
292 * @brief Returns @c true when @p T is an integer type excluding @c bool and
293 * all @c char variants.
294 *
295 * @details
296 * Matches @c short, @c int, @c long, @c long long, and their unsigned
297 * counterparts. Explicitly excludes @c bool, @c char, @c signed char,
298 * and @c unsigned char.
299 *
300 * @tparam T The type to test.
301 * @return @c true for plain integer types.
302 */
303template <typename T>
304[[nodiscard]] static constexpr bool is_integer() {
305 using U = std::remove_cv_t<T>;
306 return std::is_integral_v<U> && !std::is_same_v<U, bool> && !std::is_same_v<U, char> &&
307 !std::is_same_v<U, signed char> && !std::is_same_v<U, unsigned char>;
308}
309
310/**
311 * @brief Returns @c true when @p T is a floating-point type.
312 *
313 * @details
314 * Matches @c float, @c double, and @c long double (with CV qualifiers stripped).
315 *
316 * @tparam T The type to test.
317 * @return @c true for floating-point types.
318 */
319template <typename T>
320[[nodiscard]] static constexpr bool is_floating() {
321 return std::is_floating_point_v<std::remove_cv_t<T>>;
322}
323
324} // namespace Traits
325
326} // namespace vlink
327
328////////////////////////////////////////////////////////////////
329/// Macro Definitions
330////////////////////////////////////////////////////////////////
331
332/**
333 * @def VLINK_HAS_MEMBER(T, member)
334 * @brief Checks at compile time whether type @p T has an accessible member named @p member.
335 *
336 * @details
337 * Expands to a @c constexpr boolean expression (true/false) evaluated at compile time.
338 * Internally delegates to @c vlink::Traits::has_member.
339 *
340 * @param T The type to inspect.
341 * @param member The unquoted member name to look for.
342 *
343 * @par Example
344 * @code
345 * struct Foo { int bar; };
346 * static_assert(VLINK_HAS_MEMBER(Foo, bar));
347 * static_assert(!VLINK_HAS_MEMBER(Foo, baz));
348 * @endcode
349 */
350#define VLINK_HAS_MEMBER(T, member) \
351 vlink::Traits::has_member<T>([](auto&& obj) -> decltype((void)(obj.member), 0) { return 0; })
STL namespace.