sdbus-c++ 2.2.1
High-level C++ D-Bus library based on systemd D-Bus implementation
Loading...
Searching...
No Matches
TypeTraits.h
Go to the documentation of this file.
1
26
27#ifndef SDBUS_CXX_TYPETRAITS_H_
28#define SDBUS_CXX_TYPETRAITS_H_
29
30#include <sdbus-c++/Error.h>
31
32#include <array>
33#include <cstdint>
34#include <functional>
35#include <map>
36#include <memory>
37#include <optional>
38#ifdef __has_include
39# if __has_include(<span>)
40# include <span>
41# endif
42#endif
43#include <string>
44#include <string_view>
45#include <tuple>
46#include <type_traits>
47#include <unordered_map>
48#include <utility>
49#include <variant>
50#include <vector>
51
52// Forward declarations
53namespace sdbus {
54 class Variant;
55 template <typename... _ValueTypes> class Struct;
56 class ObjectPath;
57 class Signature;
58 class UnixFd;
59 template<typename _T1, typename _T2> using DictEntry = std::pair<_T1, _T2>;
60 class BusName;
61 class InterfaceName;
62 class MemberName;
63 class MethodCall;
64 class MethodReply;
65 class Signal;
66 class Message;
67 class PropertySetCall;
68 class PropertyGetReply;
69 template <typename... _Results> class Result;
70 class Error;
71 template <typename _T, typename _Enable = void> struct signature_of;
72}
73
74namespace sdbus {
75
76 // Callbacks from sdbus-c++
77 using method_callback = std::function<void(MethodCall msg)>;
78 using async_reply_handler = std::function<void(MethodReply reply, std::optional<Error> error)>;
79 using signal_handler = std::function<void(Signal signal)>;
80 using message_handler = std::function<void(Message msg)>;
81 using property_set_callback = std::function<void(PropertySetCall msg)>;
82 using property_get_callback = std::function<void(PropertyGetReply& reply)>;
83
84 // Type-erased RAII-style handle to callbacks/subscriptions registered to sdbus-c++
85 using Slot = std::unique_ptr<void, std::function<void(void*)>>;
86
87 // Tag specifying that an owning handle (so-called slot) of the logical resource shall be provided to the client
88 struct return_slot_t { explicit return_slot_t() = default; };
89 inline constexpr return_slot_t return_slot{};
90 // Tag specifying that the library shall own the slot resulting from the call of the function (so-called floating slot)
91 struct floating_slot_t { explicit floating_slot_t() = default; };
92 inline constexpr floating_slot_t floating_slot{};
93 // Tag denoting the assumption that the caller has already obtained message ownership
94 struct adopt_message_t { explicit adopt_message_t() = default; };
95 inline constexpr adopt_message_t adopt_message{};
96 // Tag denoting the assumption that the caller has already obtained fd ownership
97 struct adopt_fd_t { explicit adopt_fd_t() = default; };
98 inline constexpr adopt_fd_t adopt_fd{};
99 // Tag specifying that the proxy shall not run an event loop thread on its D-Bus connection.
100 // Such proxies are typically created to carry out a simple synchronous D-Bus call(s) and then are destroyed.
101 struct dont_run_event_loop_thread_t { explicit dont_run_event_loop_thread_t() = default; };
102 inline constexpr dont_run_event_loop_thread_t dont_run_event_loop_thread{};
103 // Tag denoting an asynchronous call that returns std::future as a handle
104 struct with_future_t { explicit with_future_t() = default; };
105 inline constexpr with_future_t with_future{};
106 // Tag denoting a call where the reply shouldn't be waited for
107 struct dont_expect_reply_t { explicit dont_expect_reply_t() = default; };
108 inline constexpr dont_expect_reply_t dont_expect_reply{};
109 // Tag denoting that the variant shall embed the other variant as its value, instead of creating a copy
110 struct embed_variant_t { explicit embed_variant_t() = default; };
111 inline constexpr embed_variant_t embed_variant{};
112
113 // Helper for static assert
114 template <class... _T> constexpr bool always_false = false;
115
116 // Helper operator+ for concatenation of `std::array`s
117 template <typename _T, std::size_t _N1, std::size_t _N2>
118 constexpr std::array<_T, _N1 + _N2> operator+(std::array<_T, _N1> lhs, std::array<_T, _N2> rhs);
119
120 // Template specializations for getting D-Bus signatures from C++ types
121 template <typename _T>
122 constexpr auto signature_of_v = signature_of<_T>::value;
123
124 template <typename _T, typename _Enable>
126 {
127 static constexpr bool is_valid = false;
128 static constexpr bool is_trivial_dbus_type = false;
129
130 static constexpr void* value = []
131 {
132 // See using-sdbus-c++.md, section "Extending sdbus-c++ type system",
133 // on how to teach sdbus-c++ about your custom types
134 static_assert(always_false<_T>, "Unsupported D-Bus type (specialize `signature_of` for your custom types)");
135 };
136 };
137
138 template <typename _T>
139 struct signature_of<const _T> : signature_of<_T>
140 {};
141
142 template <typename _T>
143 struct signature_of<volatile _T> : signature_of<_T>
144 {};
145
146 template <typename _T>
147 struct signature_of<const volatile _T> : signature_of<_T>
148 {};
149
150 template <typename _T>
152 {};
153
154 template <typename _T>
156 {};
157
158 template <>
159 struct signature_of<void>
160 {
161 static constexpr std::array<char, 0> value{};
162 static constexpr bool is_valid = true;
163 static constexpr bool is_trivial_dbus_type = false;
164 };
165
166 template <>
167 struct signature_of<bool>
168 {
169 static constexpr std::array value{'b'};
170 static constexpr bool is_valid = true;
171 static constexpr bool is_trivial_dbus_type = true;
172 };
173
174 template <>
175 struct signature_of<uint8_t>
176 {
177 static constexpr std::array value{'y'};
178 static constexpr bool is_valid = true;
179 static constexpr bool is_trivial_dbus_type = true;
180 };
181
182 template <>
183 struct signature_of<int16_t>
184 {
185 static constexpr std::array value{'n'};
186 static constexpr bool is_valid = true;
187 static constexpr bool is_trivial_dbus_type = true;
188 };
189
190 template <>
191 struct signature_of<uint16_t>
192 {
193 static constexpr std::array value{'q'};
194 static constexpr bool is_valid = true;
195 static constexpr bool is_trivial_dbus_type = true;
196 };
197
198 template <>
199 struct signature_of<int32_t>
200 {
201 static constexpr std::array value{'i'};
202 static constexpr bool is_valid = true;
203 static constexpr bool is_trivial_dbus_type = true;
204 };
205
206 template <>
207 struct signature_of<uint32_t>
208 {
209 static constexpr std::array value{'u'};
210 static constexpr bool is_valid = true;
211 static constexpr bool is_trivial_dbus_type = true;
212 };
213
214 template <>
215 struct signature_of<int64_t>
216 {
217 static constexpr std::array value{'x'};
218 static constexpr bool is_valid = true;
219 static constexpr bool is_trivial_dbus_type = true;
220 };
221
222 template <>
223 struct signature_of<uint64_t>
224 {
225 static constexpr std::array value{'t'};
226 static constexpr bool is_valid = true;
227 static constexpr bool is_trivial_dbus_type = true;
228 };
229
230 template <>
231 struct signature_of<double>
232 {
233 static constexpr std::array value{'d'};
234 static constexpr bool is_valid = true;
235 static constexpr bool is_trivial_dbus_type = true;
236 };
237
238 template <>
239 struct signature_of<std::string>
240 {
241 static constexpr std::array value{'s'};
242 static constexpr bool is_valid = true;
243 static constexpr bool is_trivial_dbus_type = false;
244 };
245
246 template <>
247 struct signature_of<std::string_view> : signature_of<std::string>
248 {};
249
250 template <>
253
254 template <>
256 {};
257
258 template <std::size_t _N>
259 struct signature_of<char[_N]> : signature_of<std::string>
260 {};
261
262 template <std::size_t _N>
263 struct signature_of<const char[_N]> : signature_of<std::string>
264 {};
265
266 template <>
267 struct signature_of<BusName> : signature_of<std::string>
268 {};
269
270 template <>
272 {};
273
274 template <>
275 struct signature_of<MemberName> : signature_of<std::string>
276 {};
277
278 template <typename... _ValueTypes>
279 struct signature_of<Struct<_ValueTypes...>>
280 {
281 static constexpr std::array contents = (signature_of_v<_ValueTypes> + ...);
282 static constexpr std::array value = std::array{'('} + contents + std::array{')'};
283 static constexpr char type_value{'r'}; /* Not actually used in signatures on D-Bus, see specs */
284 static constexpr bool is_valid = true;
285 static constexpr bool is_trivial_dbus_type = false;
286 };
287
288 template <>
290 {
291 static constexpr std::array value{'v'};
292 static constexpr bool is_valid = true;
293 static constexpr bool is_trivial_dbus_type = false;
294 };
295
296 template <typename... Elements>
297 struct signature_of<std::variant<Elements...>> : signature_of<Variant>
298 {};
299
300 template <>
302 {
303 static constexpr std::array value{'o'};
304 static constexpr bool is_valid = true;
305 static constexpr bool is_trivial_dbus_type = false;
306 };
307
308 template <>
310 {
311 static constexpr std::array value{'g'};
312 static constexpr bool is_valid = true;
313 static constexpr bool is_trivial_dbus_type = false;
314 };
315
316 template <>
318 {
319 static constexpr std::array value{'h'};
320 static constexpr bool is_valid = true;
321 static constexpr bool is_trivial_dbus_type = false;
322 };
323
324 template <typename _T1, typename _T2>
325 struct signature_of<DictEntry<_T1, _T2>>
326 {
327 static constexpr std::array value = std::array{'{'} + signature_of_v<std::tuple<_T1, _T2>> + std::array{'}'};
328 static constexpr char type_value{'e'}; /* Not actually used in signatures on D-Bus, see specs */
329 static constexpr bool is_valid = true;
330 static constexpr bool is_trivial_dbus_type = false;
331 };
332
333 template <typename _Element, typename _Allocator>
334 struct signature_of<std::vector<_Element, _Allocator>>
335 {
336 static constexpr std::array value = std::array{'a'} + signature_of_v<_Element>;
337 static constexpr bool is_valid = true;
338 static constexpr bool is_trivial_dbus_type = false;
339 };
340
341 template <typename _Element, std::size_t _Size>
342 struct signature_of<std::array<_Element, _Size>> : signature_of<std::vector<_Element>>
343 {
344 };
345
346#ifdef __cpp_lib_span
347 template <typename _Element, std::size_t _Extent>
348 struct signature_of<std::span<_Element, _Extent>> : signature_of<std::vector<_Element>>
349 {
350 };
351#endif
352
353 template <typename _Enum> // is_const_v and is_volatile_v to avoid ambiguity conflicts with const and volatile specializations of signature_of
354 struct signature_of<_Enum, typename std::enable_if_t<std::is_enum_v<_Enum> && !std::is_const_v<_Enum> && !std::is_volatile_v<_Enum>>>
355 : signature_of<std::underlying_type_t<_Enum>>
356 {};
357
358 template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
359 struct signature_of<std::map<_Key, _Value, _Compare, _Allocator>>
360 {
361 static constexpr std::array value = std::array{'a'} + signature_of_v<DictEntry<_Key, _Value>>;
362 static constexpr bool is_valid = true;
363 static constexpr bool is_trivial_dbus_type = false;
364 };
365
366 template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
367 struct signature_of<std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>>
368 : signature_of<std::map<_Key, _Value>>
369 {
370 };
371
372 template <typename... _Types>
373 struct signature_of<std::tuple<_Types...>> // A simple concatenation of signatures of _Types
374 {
375 static constexpr std::array value = (std::array<char, 0>{} + ... + signature_of_v<_Types>);
376 static constexpr bool is_valid = false;
377 static constexpr bool is_trivial_dbus_type = false;
378 };
379
380 // To simplify conversions of arrays to C strings
381 template <typename _T, std::size_t _N>
382 constexpr auto as_null_terminated(std::array<_T, _N> arr)
383 {
384 return arr + std::array<_T, 1>{0};
385 }
386
387 // Function traits implementation inspired by (c) kennytm,
388 // https://github.com/kennytm/utils/blob/master/traits.hpp
389 template <typename _Type>
390 struct function_traits : function_traits<decltype(&_Type::operator())>
391 {};
392
393 template <typename _Type>
394 struct function_traits<const _Type> : function_traits<_Type>
395 {};
396
397 template <typename _Type>
399 {};
400
401 template <typename _ReturnType, typename... _Args>
403 {
404 typedef _ReturnType result_type;
405 typedef std::tuple<_Args...> arguments_type;
406 typedef std::tuple<std::decay_t<_Args>...> decayed_arguments_type;
407
408 typedef _ReturnType function_type(_Args...);
409
410 static constexpr std::size_t arity = sizeof...(_Args);
411
412// template <size_t _Idx, typename _Enabled = void>
413// struct arg;
414//
415// template <size_t _Idx>
416// struct arg<_Idx, std::enable_if_t<(_Idx < arity)>>
417// {
418// typedef std::tuple_element_t<_Idx, arguments_type> type;
419// };
420//
421// template <size_t _Idx>
422// struct arg<_Idx, std::enable_if_t<!(_Idx < arity)>>
423// {
424// typedef void type;
425// };
426
427 template <size_t _Idx>
428 struct arg
429 {
430 typedef std::tuple_element_t<_Idx, std::tuple<_Args...>> type;
431 };
432
433 template <size_t _Idx>
434 using arg_t = typename arg<_Idx>::type;
435 };
436
437 template <typename _ReturnType, typename... _Args>
438 struct function_traits<_ReturnType(_Args...)> : function_traits_base<_ReturnType, _Args...>
439 {
440 static constexpr bool is_async = false;
441 static constexpr bool has_error_param = false;
442 };
443
444 template <typename... _Args>
445 struct function_traits<void(std::optional<Error>, _Args...)> : function_traits_base<void, _Args...>
446 {
447 static constexpr bool has_error_param = true;
448 };
449
450 template <typename... _Args, typename... _Results>
451 struct function_traits<void(Result<_Results...>, _Args...)> : function_traits_base<std::tuple<_Results...>, _Args...>
452 {
453 static constexpr bool is_async = true;
454 using async_result_t = Result<_Results...>;
455 };
456
457 template <typename... _Args, typename... _Results>
458 struct function_traits<void(Result<_Results...>&&, _Args...)> : function_traits_base<std::tuple<_Results...>, _Args...>
459 {
460 static constexpr bool is_async = true;
461 using async_result_t = Result<_Results...>;
462 };
463
464 template <typename _ReturnType, typename... _Args>
465 struct function_traits<_ReturnType(*)(_Args...)> : function_traits<_ReturnType(_Args...)>
466 {};
467
468 template <typename _ClassType, typename _ReturnType, typename... _Args>
469 struct function_traits<_ReturnType(_ClassType::*)(_Args...)> : function_traits<_ReturnType(_Args...)>
470 {
471 typedef _ClassType& owner_type;
472 };
473
474 template <typename _ClassType, typename _ReturnType, typename... _Args>
475 struct function_traits<_ReturnType(_ClassType::*)(_Args...) const> : function_traits<_ReturnType(_Args...)>
476 {
477 typedef const _ClassType& owner_type;
478 };
479
480 template <typename _ClassType, typename _ReturnType, typename... _Args>
481 struct function_traits<_ReturnType(_ClassType::*)(_Args...) volatile> : function_traits<_ReturnType(_Args...)>
482 {
483 typedef volatile _ClassType& owner_type;
484 };
485
486 template <typename _ClassType, typename _ReturnType, typename... _Args>
487 struct function_traits<_ReturnType(_ClassType::*)(_Args...) const volatile> : function_traits<_ReturnType(_Args...)>
488 {
489 typedef const volatile _ClassType& owner_type;
490 };
491
492 template <typename FunctionType>
493 struct function_traits<std::function<FunctionType>> : function_traits<FunctionType>
494 {};
495
496 template <class _Function>
497 constexpr auto is_async_method_v = function_traits<_Function>::is_async;
498
499 template <class _Function>
500 constexpr auto has_error_param_v = function_traits<_Function>::has_error_param;
501
502 template <typename _FunctionType>
503 using function_arguments_t = typename function_traits<_FunctionType>::arguments_type;
504
505 template <typename _FunctionType, size_t _Idx>
506 using function_argument_t = typename function_traits<_FunctionType>::template arg_t<_Idx>;
507
508 template <typename _FunctionType>
509 constexpr auto function_argument_count_v = function_traits<_FunctionType>::arity;
510
511 template <typename _FunctionType>
512 using function_result_t = typename function_traits<_FunctionType>::result_type;
513
514 template <typename _Function>
519
520 template <typename _Function>
521 using tuple_of_function_input_arg_types_t = typename tuple_of_function_input_arg_types<_Function>::type;
522
523 template <typename _Function>
528
529 template <typename _Function>
530 using tuple_of_function_output_arg_types_t = typename tuple_of_function_output_arg_types<_Function>::type;
531
532 template <typename _Function>
533 struct signature_of_function_input_arguments : signature_of<tuple_of_function_input_arg_types_t<_Function>>
534 {
535 static std::string value_as_string()
536 {
537 constexpr auto signature = as_null_terminated(signature_of_v<tuple_of_function_input_arg_types_t<_Function>>);
538 return signature.data();
539 }
540 };
541
542 template <typename _Function>
543 inline auto signature_of_function_input_arguments_v = signature_of_function_input_arguments<_Function>::value_as_string();
544
545 template <typename _Function>
546 struct signature_of_function_output_arguments : signature_of<tuple_of_function_output_arg_types_t<_Function>>
547 {
548 static std::string value_as_string()
549 {
550 constexpr auto signature = as_null_terminated(signature_of_v<tuple_of_function_output_arg_types_t<_Function>>);
551 return signature.data();
552 }
553 };
554
555 template <typename _Function>
556 inline auto signature_of_function_output_arguments_v = signature_of_function_output_arguments<_Function>::value_as_string();
557
558 // std::future stuff for return values of async calls
559 template <typename... _Args> struct future_return
560 {
561 typedef std::tuple<_Args...> type;
562 };
563
564 template <> struct future_return<>
565 {
566 typedef void type;
567 };
568
569 template <typename _Type> struct future_return<_Type>
570 {
571 typedef _Type type;
572 };
573
574 template <typename... _Args>
575 using future_return_t = typename future_return<_Args...>::type;
576
577 // Credit: Piotr Skotnicki (https://stackoverflow.com/a/57639506)
578 template <typename, typename>
579 constexpr bool is_one_of_variants_types = false;
580
581 template <typename... _VariantTypes, typename _QueriedType>
582 constexpr bool is_one_of_variants_types<std::variant<_VariantTypes...>, _QueriedType>
583 = (std::is_same_v<_QueriedType, _VariantTypes> || ...);
584
585 // Wrapper (tag) denoting we want to serialize user-defined struct
586 // into a D-Bus message as a dictionary of strings to variants.
587 template <typename _Struct>
588 struct as_dictionary
589 {
590 explicit as_dictionary(const _Struct& s) : m_struct(s) {}
591 const _Struct& m_struct;
592 };
593
594 template <typename _Type>
595 const _Type& as_dictionary_if_struct(const _Type& object)
596 {
597 return object; // identity in case _Type is not struct (user-defined structs shall provide an overload)
598 }
599
600 // By default, the dict-as-struct deserialization strategy is strict.
601 // Strict means that every key of the deserialized dictionary must have its counterpart member in the struct, otherwise an exception is thrown.
602 // Relaxed means that a key that does not have a matching struct member is silently ignored.
603 // The behavior can be overridden for user-defined struct by specializing this variable template.
604 template <typename _Struct>
605 constexpr auto strict_dict_as_struct_deserialization_v = true;
606
607 // By default, the struct-as-dict serialization strategy is single-level only (as opposed to nested).
608 // Single-level means that only the specific struct is serialized as a dictionary, serializing members that are structs always as structs.
609 // Nested means that the struct *and* its members that are structs are all serialized as a dictionary. If nested strategy is also
610 // defined for the nested struct, then the same behavior applies for that struct, recursively.
611 // The behavior can be overridden for user-defined struct by specializing this variable template.
612 template <typename _Struct>
613 constexpr auto nested_struct_as_dict_serialization_v = false;
614
615 namespace detail
616 {
617 template <class _Function, class _Tuple, typename... _Args, std::size_t... _I>
618 constexpr decltype(auto) apply_impl( _Function&& f
619 , Result<_Args...>&& r
620 , _Tuple&& t
621 , std::index_sequence<_I...> )
622 {
623 return std::forward<_Function>(f)(std::move(r), std::get<_I>(std::forward<_Tuple>(t))...);
624 }
625
626 template <class _Function, class _Tuple, std::size_t... _I>
627 decltype(auto) apply_impl( _Function&& f
628 , std::optional<Error> e
629 , _Tuple&& t
630 , std::index_sequence<_I...> )
631 {
632 return std::forward<_Function>(f)(std::move(e), std::get<_I>(std::forward<_Tuple>(t))...);
633 }
634
635 // For non-void returning functions, apply_impl simply returns function return value (a tuple of values).
636 // For void-returning functions, apply_impl returns an empty tuple.
637 template <class _Function, class _Tuple, std::size_t... _I>
638 constexpr decltype(auto) apply_impl( _Function&& f
639 , _Tuple&& t
640 , std::index_sequence<_I...> )
641 {
642 if constexpr (!std::is_void_v<function_result_t<_Function>>)
643 return std::forward<_Function>(f)(std::get<_I>(std::forward<_Tuple>(t))...);
644 else
645 return std::forward<_Function>(f)(std::get<_I>(std::forward<_Tuple>(t))...), std::tuple<>{};
646 }
647 }
648
649 // Convert tuple `t' of values into a list of arguments
650 // and invoke function `f' with those arguments.
651 template <class _Function, class _Tuple>
652 constexpr decltype(auto) apply(_Function&& f, _Tuple&& t)
653 {
654 return detail::apply_impl( std::forward<_Function>(f)
655 , std::forward<_Tuple>(t)
656 , std::make_index_sequence<std::tuple_size<std::decay_t<_Tuple>>::value>{} );
657 }
658
659 // Convert tuple `t' of values into a list of arguments
660 // and invoke function `f' with those arguments.
661 template <class _Function, class _Tuple, typename... _Args>
662 constexpr decltype(auto) apply(_Function&& f, Result<_Args...>&& r, _Tuple&& t)
663 {
664 return detail::apply_impl( std::forward<_Function>(f)
665 , std::move(r)
666 , std::forward<_Tuple>(t)
667 , std::make_index_sequence<std::tuple_size<std::decay_t<_Tuple>>::value>{} );
668 }
669
670 // Convert tuple `t' of values into a list of arguments
671 // and invoke function `f' with those arguments.
672 template <class _Function, class _Tuple>
673 decltype(auto) apply(_Function&& f, std::optional<Error> e, _Tuple&& t)
674 {
675 return detail::apply_impl( std::forward<_Function>(f)
676 , std::move(e)
677 , std::forward<_Tuple>(t)
678 , std::make_index_sequence<std::tuple_size<std::decay_t<_Tuple>>::value>{} );
679 }
680
681 // Convenient concatenation of arrays
682 template <typename _T, std::size_t _N1, std::size_t _N2>
683 constexpr std::array<_T, _N1 + _N2> operator+(std::array<_T, _N1> lhs, std::array<_T, _N2> rhs)
684 {
685 std::array<_T, _N1 + _N2> result{};
686 std::size_t index = 0;
687
688 for (auto& el : lhs) {
689 result[index] = std::move(el);
690 ++index;
691 }
692 for (auto& el : rhs) {
693 result[index] = std::move(el);
694 ++index;
695 }
696
697 return result;
698 }
699
700}
701
702#endif /* SDBUS_CXX_TYPETRAITS_H_ */
std::pair< _T1, _T2 > DictEntry
Definition Types.h:402
Definition Types.h:215
Definition Error.h:44
Definition Types.h:238
Definition Types.h:258
Definition Message.h:81
Definition Message.h:268
Definition Message.h:293
Definition Types.h:195
Definition Message.h:325
Definition Message.h:316
Definition MethodResult.h:51
Definition Message.h:304
Definition Types.h:282
Definition Types.h:153
Definition Types.h:307
Definition Types.h:57
Definition TypeTraits.h:97
Definition TypeTraits.h:94
Definition TypeTraits.h:107
Definition TypeTraits.h:101
Definition TypeTraits.h:110
Definition TypeTraits.h:91
Definition TypeTraits.h:429
Definition TypeTraits.h:403
Definition TypeTraits.h:391
Definition TypeTraits.h:560
Definition TypeTraits.h:88
Definition TypeTraits.h:126
Definition TypeTraits.h:516
Definition TypeTraits.h:104