sdbus-c++ 2.3.0
High-level C++ D-Bus library based on systemd D-Bus implementation
Loading...
Searching...
No Matches
Message.h
Go to the documentation of this file.
1
26
27#ifndef SDBUS_CXX_MESSAGE_H_
28#define SDBUS_CXX_MESSAGE_H_
29
30#include <sdbus-c++/Error.h>
32
33#include <algorithm>
34#include <array>
35#include <cassert>
36#include <cstdint>
37#include <cstring>
38#include <functional>
39#include <map>
40#ifdef __has_include
41# if __has_include(<span>)
42# include <span>
43# endif
44#endif
45#include <string>
46#include <sys/types.h>
47#include <unordered_map>
48#include <utility>
49#include <variant>
50#include <vector>
51
52// Forward declarations
53namespace sdbus {
54 class Variant;
55 class ObjectPath;
56 class Signature;
57 template <typename... ValueTypes> class Struct;
58 class UnixFd;
59 class MethodReply;
60 namespace internal {
61 class IConnection;
62 } // namespace internal
63} // namespace sdbus
64
65namespace sdbus {
66
67 /********************************************/
80 class [[nodiscard]] Message
81 {
82 public:
83 Message(const Message&) noexcept;
84 Message& operator=(const Message&) noexcept;
85 Message(Message&& other) noexcept;
86 Message& operator=(Message&& other) noexcept;
87 ~Message();
88
89 Message& operator<<(bool item);
90 Message& operator<<(int16_t item);
91 Message& operator<<(int32_t item);
92 Message& operator<<(int64_t item);
93 Message& operator<<(uint8_t item);
94 Message& operator<<(uint16_t item);
95 Message& operator<<(uint32_t item);
96 Message& operator<<(uint64_t item);
97 Message& operator<<(double item);
98 Message& operator<<(const char *item);
99 Message& operator<<(const std::string &item);
100 Message& operator<<(std::string_view item);
101 Message& operator<<(const Variant &item);
102 template <typename ...Elements>
103 Message& operator<<(const std::variant<Elements...>& value);
104 Message& operator<<(const ObjectPath &item);
105 Message& operator<<(const Signature &item);
106 Message& operator<<(const UnixFd &item);
107 template <typename Element, typename Allocator>
108 Message& operator<<(const std::vector<Element, Allocator>& items);
109 template <typename Element, std::size_t Size>
110 Message& operator<<(const std::array<Element, Size>& items);
111#ifdef __cpp_lib_span
112 template <typename Element, std::size_t Extent>
113 Message& operator<<(const std::span<Element, Extent>& items);
114#endif
115 // NOLINTNEXTLINE(modernize-use-constraints): Public API is C++17-compatible
116 template <typename Enum, typename = std::enable_if_t<std::is_enum_v<Enum>>>
117 Message& operator<<(const Enum& item);
118 template <typename Key, typename Value>
119 Message& operator<<(const DictEntry<Key, Value>& value);
120 template <typename Key, typename Value, typename Compare, typename Allocator>
121 Message& operator<<(const std::map<Key, Value, Compare, Allocator>& items);
122 template <typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator>
123 Message& operator<<(const std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& items);
124 template <typename... ValueTypes>
125 Message& operator<<(const Struct<ValueTypes...>& item);
126 template <typename... ValueTypes>
127 Message& operator<<(const std::tuple<ValueTypes...>& item);
128
129 Message& operator>>(bool& item);
130 Message& operator>>(int16_t& item);
131 Message& operator>>(int32_t& item);
132 Message& operator>>(int64_t& item);
133 Message& operator>>(uint8_t& item);
134 Message& operator>>(uint16_t& item);
135 Message& operator>>(uint32_t& item);
136 Message& operator>>(uint64_t& item);
137 Message& operator>>(double& item);
138 Message& operator>>(char*& item);
139 Message& operator>>(std::string &item);
140 Message& operator>>(Variant &item);
141 template <typename ...Elements>
142 Message& operator>>(std::variant<Elements...>& value);
143 Message& operator>>(ObjectPath &item);
144 Message& operator>>(Signature &item);
145 Message& operator>>(UnixFd &item);
146 template <typename Element, typename Allocator>
147 Message& operator>>(std::vector<Element, Allocator>& items);
148 template <typename Element, std::size_t Size>
149 Message& operator>>(std::array<Element, Size>& items);
150#ifdef __cpp_lib_span
151 template <typename Element, std::size_t Extent>
152 Message& operator>>(std::span<Element, Extent>& items);
153#endif
154 // NOLINTNEXTLINE(modernize-use-constraints): Public API is C++17-compatible
155 template <typename Enum, typename = std::enable_if_t<std::is_enum_v<Enum>>>
156 Message& operator>>(Enum& item);
157 template <typename Key, typename Value>
158 Message& operator>>(DictEntry<Key, Value>& value);
159 template <typename Key, typename Value, typename Compare, typename Allocator>
160 Message& operator>>(std::map<Key, Value, Compare, Allocator>& items);
161 template <typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator>
162 Message& operator>>(std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& items);
163 template <typename... ValueTypes>
164 Message& operator>>(Struct<ValueTypes...>& item);
165 template <typename... ValueTypes>
166 Message& operator>>(std::tuple<ValueTypes...>& item);
167
168 template <typename ElementType>
169 Message& openContainer();
170 Message& openContainer(const char* signature);
171 Message& closeContainer();
172 template <typename KeyType, typename ValueType>
173 Message& openDictEntry();
174 Message& openDictEntry(const char* signature);
175 Message& closeDictEntry();
176 template <typename ValueType>
177 Message& openVariant();
178 Message& openVariant(const char* signature);
179 Message& closeVariant();
180 template <typename... ValueTypes>
181 Message& openStruct();
182 Message& openStruct(const char* signature);
183 Message& closeStruct();
184
185 template <typename ElementType>
186 Message& enterContainer();
187 Message& enterContainer(const char* signature);
188 Message& exitContainer();
189 template <typename KeyType, typename ValueType>
190 Message& enterDictEntry();
191 Message& enterDictEntry(const char* signature);
192 Message& exitDictEntry();
193 template <typename ValueType>
194 Message& enterVariant();
195 Message& enterVariant(const char* signature);
196 Message& exitVariant();
197 template <typename... ValueTypes>
198 Message& enterStruct();
199 Message& enterStruct(const char* signature);
200 Message& exitStruct();
201
202 Message& appendArray(char type, const void *ptr, size_t size);
203 Message& readArray(char type, const void **ptr, size_t *size);
204
205 template <typename Key, typename Value, typename Callback>
206 Message& serializeDictionary(const Callback& callback);
207 template <typename Key, typename Value>
208 Message& serializeDictionary(const std::initializer_list<DictEntry<Key, Value>>& dictEntries);
209 template <typename Key, typename Value, typename Callback>
210 Message& deserializeDictionary(const Callback& callback);
211
212 explicit operator bool() const;
213 void clearFlags();
214
215 const char* getInterfaceName() const;
216 const char* getMemberName() const;
217 const char* getSender() const;
218 const char* getPath() const;
219 const char* getDestination() const;
220 uint64_t getCookie() const;
221 // TODO: short docs in whole Message API
222 std::pair<char, const char*> peekType() const;
223 bool isValid() const;
224 bool isEmpty() const;
225 bool isAtEnd(bool complete) const;
226
227 void copyTo(Message& destination, bool complete) const;
228 void seal();
229 void rewind(bool complete);
230
231 enum class DumpFlags : uint64_t // NOLINT(performance-enum-size): using size from sd-bus
232 {
233 Default = 0ULL,
234 WithHeader = 1ULL << 0,
235 SubtreeOnly = 1ULL << 1,
236 SubtreeOnlyWithHeader = WithHeader | SubtreeOnly
237 };
238 [[nodiscard]] std::string dumpToString(DumpFlags flags) const;
239
240 pid_t getCredsPid() const;
241 uid_t getCredsUid() const;
242 uid_t getCredsEuid() const;
243 gid_t getCredsGid() const;
244 gid_t getCredsEgid() const;
245 std::vector<gid_t> getCredsSupplementaryGids() const;
246 std::string getSELinuxContext() const;
247
248 class Factory;
249
250 private:
251 template <typename Array>
252 void serializeArray(const Array& items);
253 template <typename Array>
254 void deserializeArray(Array& items);
255 template <typename Array>
256 void deserializeArrayFast(Array& items);
257 template <typename Element, typename Allocator>
258 void deserializeArrayFast(std::vector<Element, Allocator>& items);
259 template <typename Array>
260 void deserializeArraySlow(Array& items);
261 template <typename Element, typename Allocator>
262 void deserializeArraySlow(std::vector<Element, Allocator>& items);
263
264 protected:
265 Message() = default;
266 explicit Message(internal::IConnection* connection) noexcept;
267 Message(void *msg, internal::IConnection* connection) noexcept;
268 Message(void *msg, internal::IConnection* connection, adopt_message_t) noexcept;
269
270 friend Factory;
271
272 void* msg_{};
273 internal::IConnection* connection_{};
274 mutable bool ok_{true};
275 };
276
277 class MethodCall : public Message
278 {
279 using Message::Message;
280 friend Factory;
281
282 public:
283 MethodCall() = default;
284
285 MethodReply send(uint64_t timeout) const;
286 [[nodiscard]] Slot send(void* callback, void* userData, uint64_t timeout, return_slot_t) const;
287
288 MethodReply createReply() const;
289 MethodReply createErrorReply(const Error& error) const;
290
291 void dontExpectReply();
292 bool doesntExpectReply() const;
293
294 protected:
295 MethodCall(void *msg, internal::IConnection* connection, adopt_message_t) noexcept;
296
297 private:
298 MethodReply sendWithReply(uint64_t timeout = 0) const;
299 MethodReply sendWithNoReply() const;
300 };
301
302 class MethodReply : public Message
303 {
304 using Message::Message;
305 friend Factory;
306
307 public:
308 MethodReply() = default;
309 void send() const;
310 uint64_t getReplyCookie() const;
311 };
312
313 class Signal : public Message
314 {
315 using Message::Message;
316 friend Factory;
317
318 public:
319 Signal() = default;
320 void setDestination(const std::string& destination);
321 void setDestination(const char* destination);
322 void send() const;
323 };
324
325 class PropertySetCall : public Message
326 {
327 using Message::Message;
328 friend Factory;
329
330 public:
331 PropertySetCall() = default;
332 };
333
334 class PropertyGetReply : public Message
335 {
336 using Message::Message;
337 friend Factory;
338
339 public:
340 PropertyGetReply() = default;
341 };
342
343 // Represents any of the above message types, or just a message that serves as a container for data
344 class PlainMessage : public Message
345 {
346 using Message::Message;
347 friend Factory;
348
349 public:
350 PlainMessage() = default;
351 };
352
353 PlainMessage createPlainMessage();
354
355 template <typename ...Elements>
356 inline Message& Message::operator<<(const std::variant<Elements...>& value)
357 {
358 std::visit([this](const auto& inner)
359 {
360 openVariant<decltype(inner)>();
361 *this << inner;
362 closeVariant();
363 }, value);
364
365 return *this;
366 }
367
368 template <typename Element, typename Allocator>
369 inline Message& Message::operator<<(const std::vector<Element, Allocator>& items)
370 {
371 serializeArray(items);
372
373 return *this;
374 }
375
376 template <typename Element, std::size_t Size>
377 inline Message& Message::operator<<(const std::array<Element, Size>& items)
378 {
379 serializeArray(items);
380
381 return *this;
382 }
383
384#ifdef __cpp_lib_span
385 template <typename Element, std::size_t Extent>
386 inline Message& Message::operator<<(const std::span<Element, Extent>& items)
387 {
388 serializeArray(items);
389
390 return *this;
391 }
392#endif
393
394 template <typename Enum, typename>
395 inline Message& Message::operator<<(const Enum &item)
396 {
397 return operator<<(static_cast<std::underlying_type_t<Enum>>(item));
398 }
399
400 template <typename Array>
401 inline void Message::serializeArray(const Array& items)
402 {
403 using ElementType = typename Array::value_type;
404
405 // Use faster, one-step serialization of contiguous array of elements of trivial D-Bus types except bool,
406 // otherwise use step-by-step serialization of individual elements.
407 if constexpr (signature_of<ElementType>::is_trivial_dbus_type && !std::is_same_v<ElementType, bool>)
408 {
409 constexpr auto signature = as_null_terminated(signature_of_v<ElementType>);
410 appendArray(*signature.data(), items.data(), items.size() * sizeof(ElementType));
411 }
412 else
413 {
414 openContainer<ElementType>();
415
416 for (const auto& item : items)
417 *this << item;
418
419 closeContainer();
420 }
421 }
422
423 template <typename Key, typename Value>
424 inline Message& Message::operator<<(const DictEntry<Key, Value>& value)
425 {
426 openDictEntry<Key, Value>();
427 *this << value.first;
428 *this << value.second;
429 closeDictEntry();
430
431 return *this;
432 }
433
434 template <typename Key, typename Value, typename Compare, typename Allocator>
435 inline Message& Message::operator<<(const std::map<Key, Value, Compare, Allocator>& items)
436 {
437 serializeDictionary<Key, Value>([&items](Message& msg){ for (const auto& item : items) msg << item; });
438
439 return *this;
440 }
441
442 template <typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator>
443 inline Message& Message::operator<<(const std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& items)
444 {
445 serializeDictionary<Key, Value>([&items](Message& msg){ for (const auto& item : items) msg << item; });
446
447 return *this;
448 }
449
450 template <typename Key, typename Value>
451 inline Message& Message::serializeDictionary(const std::initializer_list<DictEntry<Key, Value>>& dictEntries)
452 {
453 serializeDictionary<Key, Value>([&](Message& msg){ for (const auto& entry : dictEntries) msg << entry; });
454
455 return *this;
456 }
457
458 template <typename Key, typename Value, typename Callback>
459 inline Message& Message::serializeDictionary(const Callback& callback)
460 {
461 openContainer<DictEntry<Key, Value>>();
462 callback(*this);
463 closeContainer();
464
465 return *this;
466 }
467
468 namespace detail
469 {
470 template <typename... Args>
471 void serialize_pack(Message& msg, Args&&... args)
472 {
473 (void)(msg << ... << std::forward<Args>(args));
474 }
475
476 template <class Tuple, std::size_t... Is>
477 void serialize_tuple( Message& msg
478 , const Tuple& tuple
479 , std::index_sequence<Is...>)
480 {
481 serialize_pack(msg, std::get<Is>(tuple)...);
482 }
483 } // namespace detail
484
485 template <typename... ValueTypes>
486 inline Message& Message::operator<<(const Struct<ValueTypes...>& item)
487 {
488 openStruct<ValueTypes...>();
489 detail::serialize_tuple(*this, item, std::index_sequence_for<ValueTypes...>{});
490 closeStruct();
491
492 return *this;
493 }
494
495 template <typename... ValueTypes>
496 inline Message& Message::operator<<(const std::tuple<ValueTypes...>& item)
497 {
498 detail::serialize_tuple(*this, item, std::index_sequence_for<ValueTypes...>{});
499 return *this;
500 }
501
502 namespace detail
503 {
504 template <typename Element, typename... Elements>
505 bool deserialize_variant(Message& msg, std::variant<Elements...>& value, const char* signature)
506 {
507 constexpr auto elemSignature = as_null_terminated(signature_of_v<Element>);
508 if (std::strcmp(signature, elemSignature.data()) != 0)
509 return false;
510
511 Element temp;
512 msg.enterVariant(signature);
513 msg >> temp;
514 msg.exitVariant();
515 value = std::move(temp);
516 return true;
517 }
518 } // namespace detail
519
520 template <typename... Elements>
521 inline Message& Message::operator>>(std::variant<Elements...>& value)
522 {
523 auto [type, contents] = peekType();
524 const bool result = (detail::deserialize_variant<Elements>(*this, value, contents) || ...);
525 SDBUS_THROW_ERROR_IF(!result, "Failed to deserialize variant: signature did not match any of the variant types", EINVAL);
526 return *this;
527 }
528
529 template <typename Element, typename Allocator>
530 inline Message& Message::operator>>(std::vector<Element, Allocator>& items)
531 {
532 deserializeArray(items);
533
534 return *this;
535 }
536
537 template <typename Element, std::size_t Size>
538 inline Message& Message::operator>>(std::array<Element, Size>& items)
539 {
540 deserializeArray(items);
541
542 return *this;
543 }
544
545#ifdef __cpp_lib_span
546 template <typename Element, std::size_t Extent>
547 inline Message& Message::operator>>(std::span<Element, Extent>& items)
548 {
549 deserializeArray(items);
550
551 return *this;
552 }
553#endif
554
555 template <typename Enum, typename>
556 inline Message& Message::operator>>(Enum& item)
557 {
558 std::underlying_type_t<Enum> val;
559 *this >> val;
560 item = static_cast<Enum>(val);
561 return *this;
562 }
563
564 template <typename Array>
565 inline void Message::deserializeArray(Array& items)
566 {
567 using ElementType = typename Array::value_type;
568
569 // Use faster, one-step deserialization of contiguous array of elements of trivial D-Bus types except bool,
570 // otherwise use step-by-step deserialization of individual elements.
571 if constexpr (signature_of<ElementType>::is_trivial_dbus_type && !std::is_same_v<ElementType, bool>)
572 {
573 deserializeArrayFast(items);
574 }
575 else
576 {
577 deserializeArraySlow(items);
578 }
579 }
580
581 template <typename Array>
582 inline void Message::deserializeArrayFast(Array& items)
583 {
584 using ElementType = typename Array::value_type;
585
586 size_t arraySize{};
587 const ElementType* arrayPtr{};
588
589 constexpr auto signature = as_null_terminated(signature_of_v<ElementType>);
590 readArray(*signature.data(), reinterpret_cast<const void**>(&arrayPtr), &arraySize);
591
592 const size_t elementsInMsg = arraySize / sizeof(ElementType);
593 const bool notEnoughSpace = items.size() < elementsInMsg;
594 SDBUS_THROW_ERROR_IF(notEnoughSpace, "Failed to deserialize array: not enough space in destination sequence", EINVAL);
595
596 std::copy_n(arrayPtr, elementsInMsg, items.begin());
597 }
598
599 template <typename Element, typename Allocator>
600 void Message::deserializeArrayFast(std::vector<Element, Allocator>& items)
601 {
602 size_t arraySize{};
603 const Element* arrayPtr{};
604
605 constexpr auto signature = as_null_terminated(signature_of_v<Element>);
606 readArray(*signature.data(), reinterpret_cast<const void**>(&arrayPtr), &arraySize);
607
608 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
609 items.insert(items.end(), arrayPtr, arrayPtr + (arraySize / sizeof(Element)));
610 }
611
612 template <typename Array>
613 inline void Message::deserializeArraySlow(Array& items)
614 {
615 using ElementType = typename Array::value_type;
616
617 if(!enterContainer<ElementType>())
618 return;
619
620 for (auto& elem : items)
621 if (!(*this >> elem))
622 break; // Keep the rest in the destination sequence untouched
623
624 SDBUS_THROW_ERROR_IF(!isAtEnd(false), "Failed to deserialize array: not enough space in destination sequence", EINVAL);
625
626 clearFlags();
627
628 exitContainer();
629 }
630
631 template <typename Element, typename Allocator>
632 void Message::deserializeArraySlow(std::vector<Element, Allocator>& items)
633 {
634 if(!enterContainer<Element>())
635 return;
636
637 while (true)
638 {
639 Element elem;
640 if (*this >> elem)
641 items.emplace_back(std::move(elem));
642 else
643 break;
644 }
645
646 clearFlags();
647
648 exitContainer();
649 }
650
651 template <typename Key, typename Value>
652 inline Message& Message::operator>>(DictEntry<Key, Value>& value)
653 {
654 if (!enterDictEntry<Key, Value>())
655 return *this;
656 *this >> value.first >> value.second;
657 exitDictEntry();
658
659 return *this;
660 }
661
662 template <typename Key, typename Value, typename Compare, typename Allocator>
663 inline Message& Message::operator>>(std::map<Key, Value, Compare, Allocator>& items)
664 {
665 deserializeDictionary<Key, Value>([&items](auto dictEntry){ items.insert(std::move(dictEntry)); });
666
667 return *this;
668 }
669
670 template <typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator>
671 inline Message& Message::operator>>(std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& items)
672 {
673 deserializeDictionary<Key, Value>([&items](auto dictEntry){ items.insert(std::move(dictEntry)); });
674
675 return *this;
676 }
677
678 template <typename Key, typename Value, typename Callback>
679 inline Message& Message::deserializeDictionary(const Callback& callback)
680 {
681 if (!enterContainer<DictEntry<Key, Value>>())
682 return *this;
683
684 while (true)
685 {
686 DictEntry<Key, Value> dictEntry;
687 *this >> dictEntry;
688 if (!*this)
689 break;
690 callback(std::move(dictEntry));
691 }
692 clearFlags();
693
694 exitContainer();
695
696 return *this;
697 }
698
699 namespace detail
700 {
701 template <typename... Args>
702 void deserialize_pack(Message& msg, Args&... args)
703 {
704 (void)(msg >> ... >> args);
705 }
706
707 template <class Tuple, std::size_t... Is>
708 void deserialize_tuple( Message& msg
709 , Tuple& tuple
710 , std::index_sequence<Is...> )
711 {
712 deserialize_pack(msg, std::get<Is>(tuple)...);
713 }
714 } // namespace detail
715
716 template <typename... ValueTypes>
717 inline Message& Message::operator>>(Struct<ValueTypes...>& item)
718 {
719 if (!enterStruct<ValueTypes...>())
720 return *this;
721
722 detail::deserialize_tuple(*this, item, std::index_sequence_for<ValueTypes...>{});
723
724 exitStruct();
725
726 return *this;
727 }
728
729 template <typename... ValueTypes>
730 inline Message& Message::operator>>(std::tuple<ValueTypes...>& item)
731 {
732 detail::deserialize_tuple(*this, item, std::index_sequence_for<ValueTypes...>{});
733 return *this;
734 }
735
736 template <typename ElementType>
737 inline Message& Message::openContainer()
738 {
739 constexpr auto signature = as_null_terminated(signature_of_v<ElementType>);
740 return openContainer(signature.data());
741 }
742
743 template <typename KeyType, typename ValueType>
744 inline Message& Message::openDictEntry()
745 {
746 constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<KeyType, ValueType>>);
747 return openDictEntry(signature.data());
748 }
749
750 template <typename ValueType>
751 inline Message& Message::openVariant()
752 {
753 constexpr auto signature = as_null_terminated(signature_of_v<ValueType>);
754 return openVariant(signature.data());
755 }
756
757 template <typename... ValueTypes>
758 inline Message& Message::openStruct()
759 {
760 constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<ValueTypes...>>);
761 return openStruct(signature.data());
762 }
763
764 template <typename ElementType>
765 inline Message& Message::enterContainer()
766 {
767 constexpr auto signature = as_null_terminated(signature_of_v<ElementType>);
768 return enterContainer(signature.data());
769 }
770
771 template <typename KeyType, typename ValueType>
772 inline Message& Message::enterDictEntry()
773 {
774 constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<KeyType, ValueType>>);
775 return enterDictEntry(signature.data());
776 }
777
778 template <typename ValueType>
779 inline Message& Message::enterVariant()
780 {
781 constexpr auto signature = as_null_terminated(signature_of_v<ValueType>);
782 return enterVariant(signature.data());
783 }
784
785 template <typename... ValueTypes>
786 inline Message& Message::enterStruct()
787 {
788 constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<ValueTypes...>>);
789 return enterStruct(signature.data());
790 }
791
792} // namespace sdbus
793
794#endif /* SDBUS_CXX_MESSAGE_H_ */
std::pair< T1, T2 > DictEntry
Definition Types.h:415
Definition Error.h:44
Definition Message.h:81
Definition Message.h:303
Definition Types.h:208
Definition Message.h:345
Definition Types.h:295
Definition Types.h:160
Definition Types.h:320
Definition Types.h:56
Definition TypeTraits.h:94
Definition TypeTraits.h:88