VLink  2.0.0
A high-performance communication middleware
traits.h
浏览该文件的文档.
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 helpers used across the VLink codebase.
27  *
28  * @details
29  * @c vlink::Traits collects a small set of self-contained template meta-functions that
30  * extend the C++ standard @c <type_traits>. Every helper follows the standard pattern of
31  * either inheriting from @c std::true_type / @c std::false_type or returning a @c bool
32  * from a @c constexpr function. The macro @c VLINK_HAS_MEMBER ties into
33  * @c has_member to detect named data or function members at compile time.
34  *
35  * Trait reference:
36  *
37  * | Helper | What it detects |
38  * | ----------------------- | ---------------------------------------------------------------- |
39  * | @c EmptyType | An empty tag type used as a no-op placeholder. |
40  * | @c ExpectFalse<T> | Always @c std::false_type; used for dependent @c static_assert. |
41  * | @c Callable<T> | @c T can be invoked with no arguments. |
42  * | @c Assignable<OT,T> | @c OT can be assigned a value of type @c T. |
43  * | @c EqualityComparable | @c OT supports @c operator== with @c T. |
44  * | @c GreaterComparable | @c OT supports both @c operator< and @c operator> with @c T. |
45  * | @c Operatorable<OT,T> | @c OT supports stream insertion and extraction with @c T. |
46  * | @c IsAtomic<T> | @c T is a @c std::atomic specialisation. |
47  * | @c IsSharedPtr<T> | @c T is a @c std::shared_ptr (or derived) specialisation. |
48  * | @c RemoveSharedPtr<T> | Removes a single @c std::shared_ptr wrapper from @c T. |
49  * | @c has_member<T>(f) | Compile-time member-access probe used by @c VLINK_HAS_MEMBER. |
50  * | @c is_non_char_ptr<T>() | @c T decays to a pointer other than @c char*. |
51  * | @c is_integer<T>() | @c T is an integer excluding @c bool and the @c char variants. |
52  * | @c is_floating<T>() | @c T is a floating-point type. |
53  *
54  * @par Example
55  * @code
56  * struct Foo { int bar; void baz() {} };
57  *
58  * static_assert( VLINK_HAS_MEMBER(Foo, bar));
59  * static_assert(!VLINK_HAS_MEMBER(Foo, qux));
60  * static_assert( VLINK_HAS_MEMBER(Foo, baz()));
61  *
62  * static_assert(vlink::Traits::Callable<decltype([] {})>::value);
63  * static_assert(vlink::Traits::IsAtomic<std::atomic<int>>::value);
64  * @endcode
65  */
66 
67 #pragma once
68 
69 #include <atomic>
70 #include <memory>
71 #include <type_traits>
72 
73 namespace vlink {
74 
75 /**
76  * @namespace vlink::Traits
77  * @brief Collection of compile-time type-trait helpers for VLink.
78  */
79 namespace Traits { // NOLINT(readability-identifier-naming)
80 
81 /**
82  * @struct EmptyType
83  * @brief Empty tag type used wherever a type parameter must be supplied but carries no payload.
84  *
85  * @details
86  * Acts as the default detail type in places such as the Logger so the meta-programming
87  * machinery has a single concrete identity to bind to.
88  */
89 struct EmptyType {};
90 
91 /**
92  * @struct ExpectFalse
93  * @brief Type trait that always derives from @c std::false_type for any @p T.
94  *
95  * @details
96  * Used inside @c static_assert to produce a dependent false expression, which prevents
97  * the compiler from instantiating a primary template when only a specialisation is
98  * valid.
99  *
100  * @tparam T Arbitrary placeholder type; the value is always false.
101  */
102 template <typename T>
103 struct ExpectFalse : std::false_type {};
104 
105 /**
106  * @struct Callable
107  * @brief Detects whether @p T can be invoked with no arguments.
108  *
109  * @tparam T Type to probe.
110  */
111 template <typename T, typename = void>
112 struct Callable : std::false_type {};
113 
114 template <typename T>
115 struct Callable<T, std::void_t<decltype(std::declval<T>()())>> : std::true_type {};
116 
117 /**
118  * @struct Assignable
119  * @brief Detects whether a value of type @p T can be assigned to an instance of @p OT.
120  *
121  * @tparam OT Target type on the left of @c =.
122  * @tparam T Source type on the right of @c =.
123  */
124 template <typename OT, typename T, typename = void>
125 struct Assignable : std::false_type {};
126 
127 template <typename OT, typename T>
128 struct Assignable<OT, T, std::void_t<decltype(std::declval<OT&>() = std::declval<T>())>> : std::true_type {};
129 
130 /**
131  * @struct EqualityComparable
132  * @brief Detects whether @p OT supports @c operator== with @p T.
133  *
134  * @tparam OT Left-hand side type.
135  * @tparam T Right-hand side type.
136  */
137 template <typename OT, typename T, typename = void>
138 struct EqualityComparable : std::false_type {};
139 
140 template <typename OT, typename T>
141 struct EqualityComparable<OT, T, std::void_t<decltype(std::declval<OT>() == std::declval<T>())>> : std::true_type {};
142 
143 /**
144  * @struct GreaterComparable
145  * @brief Detects whether @p OT supports both @c operator< and @c operator> with @p T.
146  *
147  * @tparam OT Left-hand side type.
148  * @tparam T Right-hand side type.
149  */
150 template <typename OT, typename T, typename = void>
152 
153 template <typename OT, typename T>
155  OT, T,
156  std::void_t<decltype(std::declval<OT&>() < std::declval<T>()), decltype(std::declval<OT&>() > std::declval<T&>())>>
157  : std::true_type {};
158 
159 /**
160  * @struct Operatorable
161  * @brief Detects whether @p OT supports stream insertion (<<) and extraction (>>) with @p T.
162  *
163  * @tparam OT Stream-like type.
164  * @tparam T Value type appearing on the right of the stream operator.
165  */
166 template <typename OT, typename T, typename = void>
167 struct Operatorable : ExpectFalse<OT> {};
168 
169 template <typename OT, typename T>
170 struct Operatorable<OT, T,
171  std::void_t<decltype(std::declval<OT&>() << std::declval<T>()),
172  decltype(std::declval<OT&>() >> std::declval<T&>())>> : std::true_type {};
173 
174 /**
175  * @struct IsAtomic
176  * @brief Detects whether @p T is a @c std::atomic specialisation.
177  *
178  * @tparam T Candidate type.
179  */
180 template <typename T>
181 struct IsAtomic : std::false_type {};
182 
183 template <typename T>
184 struct IsAtomic<std::atomic<T>> : std::true_type {};
185 
186 /**
187  * @struct IsSharedPtr
188  * @brief Detects whether @p T is (or derives from) a @c std::shared_ptr specialisation.
189  *
190  * @tparam T Candidate type.
191  */
192 template <typename T, typename = void>
193 struct IsSharedPtr : std::false_type {};
194 
195 template <typename T>
196 struct IsSharedPtr<T, std::void_t<typename T::element_type>>
197  : std::is_base_of<std::shared_ptr<typename T::element_type>, T> {};
198 
199 /**
200  * @struct RemoveSharedPtr
201  * @brief Removes a single @c std::shared_ptr wrapper from @p T, leaving non-pointer types unchanged.
202  *
203  * @details
204  * When @p T is a @c std::shared_ptr, @c Type is the contained element type; otherwise
205  * @c Type is @p T verbatim.
206  *
207  * @tparam T Source type.
208  */
209 template <typename T, bool = IsSharedPtr<T>::value>
211  using Type = T; ///< Resolved type after optional shared_ptr removal.
212 };
213 
214 template <typename T>
215 struct RemoveSharedPtr<T, true> {
216  using Type = typename T::element_type; ///< Element type unwrapped from the shared_ptr.
217 };
218 
219 /**
220  * @brief Compile-time probe that succeeds when @p f is a well-formed expression on a default-constructed @p T.
221  *
222  * @details
223  * Used as a building block for @c VLINK_HAS_MEMBER. The lambda passed as @p f must take a
224  * forwarding reference and reference the candidate member with @c decltype-style sfinae.
225  *
226  * @tparam T Probed type.
227  * @tparam F Lambda type.
228  * @param f Probe lambda.
229  * @return @c true when the lambda is well-formed for @p T, @c false otherwise.
230  *
231  * @note Prefer the @c VLINK_HAS_MEMBER macro for member-name checks.
232  */
233 template <typename T, typename F>
234 [[nodiscard]] static constexpr auto has_member(F&& f) noexcept -> decltype(f(std::declval<T>()), true) {
235  return true;
236 }
237 
238 template <typename>
239 // NOLINTNEXTLINE(modernize-avoid-variadic-functions)
240 [[nodiscard]] static constexpr auto has_member(...) noexcept {
241  return false;
242 }
243 
244 /**
245  * @brief Returns @c true when @p T decays to a pointer type other than @c char*.
246  *
247  * @details
248  * Used internally by VLink serialisation to distinguish raw data pointers from
249  * C-strings.
250  *
251  * @tparam T Type under test.
252  * @return @c true for non-char pointer types.
253  */
254 template <typename T>
255 [[nodiscard]] static constexpr auto is_non_char_ptr() noexcept {
256  return std::is_pointer_v<std::decay_t<T>> &&
257  !std::is_same_v<std::remove_cv_t<std::remove_pointer_t<std::decay_t<T>>>, char>;
258 }
259 
260 /**
261  * @brief Returns @c true for plain integer types, excluding @c bool and every @c char variant.
262  *
263  * @details
264  * Accepts @c short, @c int, @c long, @c long @c long and their unsigned counterparts.
265  * Explicitly rejects @c bool, @c char, @c signed @c char and @c unsigned @c char.
266  *
267  * @tparam T Type under test.
268  * @return @c true for the accepted integer set.
269  */
270 template <typename T>
271 [[nodiscard]] static constexpr bool is_integer() {
272  using U = std::remove_cv_t<T>;
273  return std::is_integral_v<U> && !std::is_same_v<U, bool> && !std::is_same_v<U, char> &&
274  !std::is_same_v<U, signed char> && !std::is_same_v<U, unsigned char>;
275 }
276 
277 /**
278  * @brief Returns @c true when @p T is a floating-point type after stripping CV qualifiers.
279  *
280  * @tparam T Type under test.
281  * @return @c true for @c float, @c double, @c long @c double.
282  */
283 template <typename T>
284 [[nodiscard]] static constexpr bool is_floating() {
285  return std::is_floating_point_v<std::remove_cv_t<T>>;
286 }
287 
288 } // namespace Traits
289 
290 } // namespace vlink
291 
292 ////////////////////////////////////////////////////////////////
293 /// Macro Definitions
294 ////////////////////////////////////////////////////////////////
295 
296 /**
297  * @def VLINK_HAS_MEMBER(T, member)
298  * @brief Compile-time check that @p T has an accessible member named @p member.
299  *
300  * @details
301  * Expands to a @c constexpr boolean expression. Use call syntax (@c member()) to probe
302  * for a member function rather than a data member. Internally delegates to
303  * @c vlink::Traits::has_member.
304  *
305  * @param T Type to inspect.
306  * @param member Member name (or @c member() for a callable probe).
307  *
308  * @par Example
309  * @code
310  * struct Foo { int bar; void baz() {} };
311  * static_assert( VLINK_HAS_MEMBER(Foo, bar));
312  * static_assert(!VLINK_HAS_MEMBER(Foo, qux));
313  * static_assert( VLINK_HAS_MEMBER(Foo, baz()));
314  * @endcode
315  */
316 #define VLINK_HAS_MEMBER(T, member) \
317  vlink::Traits::has_member<T>([](auto&& obj) -> decltype((void)(obj.member), 0) { return 0; })