1 #pragma once 2 3 #include <array> 4 #include <atomic> 5 #include <cstddef> 6 #include <cstdint> 7 #include <memory> 8 #include <mutex> 9 #include <ostream> 10 #include <string> 11 #include <type_traits> 12 #include <vector> 13 14 #include <c10/macros/Export.h> 15 #include <c10/macros/Macros.h> 16 #include <c10/util/Exception.h> 17 #include <c10/util/Half.h> 18 #include <c10/util/IdWrapper.h> 19 #include <c10/util/TypeIndex.h> 20 #include <c10/util/TypeTraits.h> 21 #include <c10/util/irange.h> 22 #include <c10/util/string_view.h> 23 24 #include <c10/core/ScalarType.h> 25 26 /* 27 * TypeIdentifier is a small type containing an id. 28 * Types must be registered using CAFFE_DECLARE_KNOWN_TYPE() (in their header) 29 * and CAFFE_DEFINE_KNOWN_TYPE() (in their .cpp file) for them to have a type 30 * id. If a type is registered, you can also create an object containing meta 31 * data like constructor, destructor, stringified name, ... about the type by 32 * calling TypeMeta::Make<T>. This returns a TypeMeta() object, which is 33 * basically just a pointer to the type information, so it's cheap to pass 34 * around. 35 */ 36 37 // TODO: This file is still in the caffe2 namespace, despite living 38 // in the ATen directory. This is because the macro 39 // CAFFE_KNOWN_TYPE (and CAFFE_DECLARE_KNOWN_TYPE) defines a template 40 // specialization, which relies 41 // on the namespace of TypeMeta matching the namespace where the macro is 42 // called. This requires us to fix all of the call-sites, which I want to do 43 // later. So the namespace is not fixed at the moment. 44 45 // Make at::Half a fundamental type. 46 47 namespace c10::guts { 48 template <> 49 struct is_fundamental<at::Half> : std::true_type {}; 50 } // namespace c10::guts 51 52 namespace caffe2 { 53 54 /** 55 * A type id is a unique id for a given C++ type. 56 * You need to register your types using CAFFE_KNOWN_TYPE(MyType) to be able to 57 * use TypeIdentifier with custom types. This is for example used to store the 58 * dtype of tensors. 59 */ 60 class C10_API TypeIdentifier final 61 : public at::IdWrapper<TypeIdentifier, c10::util::type_index> { 62 public: 63 friend std::ostream& operator<<(std::ostream& stream, TypeIdentifier typeId); 64 friend constexpr bool operator<(TypeIdentifier lhs, TypeIdentifier rhs); 65 66 /** 67 * Returns the unique id for the given type T. The id is unique for the type T 68 * in the sense that for any two different types, their ids are different; for 69 * the same type T, the id remains the same over different calls of the 70 * function. However, this is not guaranteed over different runs, as the id 71 * is generated during run-time. Do NOT serialize the id for storage. 72 */ 73 template <typename T> 74 static C10_HOST_CONSTEXPR_EXCEPT_WIN_CUDA TypeIdentifier Get() noexcept { 75 return TypeIdentifier(c10::util::get_type_index<T>()); 76 } 77 78 static constexpr TypeIdentifier uninitialized() { 79 return TypeIdentifier(c10::util::type_index{0}); 80 } 81 82 private: 83 constexpr explicit TypeIdentifier(c10::util::type_index id) : IdWrapper(id) {} 84 }; 85 86 // Allow usage in std::map / std::set 87 // TODO Disallow this and rather use std::unordered_map/set everywhere 88 inline constexpr bool operator<(TypeIdentifier lhs, TypeIdentifier rhs) { 89 return lhs.underlyingId() < rhs.underlyingId(); 90 } 91 92 inline std::ostream& operator<<( 93 std::ostream& stream, 94 caffe2::TypeIdentifier typeId) { 95 return stream << typeId.underlyingId(); 96 } 97 98 } // namespace caffe2 99 100 namespace at { 101 using DataType = caffe2::TypeIdentifier; 102 } 103 104 C10_DEFINE_HASH_FOR_IDWRAPPER(caffe2::TypeIdentifier) 105 106 namespace caffe2 { 107 108 namespace detail { 109 110 // This struct holds the actual type information. There will be 111 // one allocated per type. TypeMeta objects will then point to the struct 112 // instance for the type they're configured for. 113 struct TypeMetaData final { 114 using New = void*(); 115 using PlacementNew = void(void*, size_t); 116 using Copy = void(const void*, void*, size_t); 117 using PlacementDelete = void(void*, size_t); 118 using Delete = void(void*); 119 120 constexpr TypeMetaData() noexcept 121 : itemsize_(0), 122 new_(nullptr), 123 placementNew_(nullptr), 124 copy_(nullptr), 125 placementDelete_(nullptr), 126 delete_(nullptr), 127 id_(TypeIdentifier::uninitialized()), 128 name_("nullptr (uninitialized)") {} 129 130 constexpr TypeMetaData( 131 size_t itemsize, 132 New* newFn, 133 PlacementNew* placementNew, 134 Copy* copy, 135 PlacementDelete* placementDelete, 136 Delete* deleteFn, 137 TypeIdentifier id, 138 c10::string_view name) noexcept 139 : itemsize_(itemsize), 140 new_(newFn), 141 placementNew_(placementNew), 142 copy_(copy), 143 placementDelete_(placementDelete), 144 delete_(deleteFn), 145 id_(id), 146 name_(name) {} 147 148 size_t itemsize_; 149 New* new_; 150 PlacementNew* placementNew_; 151 Copy* copy_; 152 PlacementDelete* placementDelete_; 153 Delete* delete_; 154 TypeIdentifier id_; 155 c10::string_view name_; 156 }; 157 158 // Mechanism for throwing errors which can't be prevented at compile time 159 // due to type erasure. E.g. somebody calling TypeMeta::copy() for 160 // non-copyable type. Right now just throws exception but is implemented 161 // in .cpp to manage dependencies 162 [[noreturn]] C10_API void _ThrowRuntimeTypeLogicError(const std::string& msg); 163 164 /** 165 * Placement new function for the type. 166 */ 167 template <typename T> 168 inline void _PlacementNew(void* ptr, size_t n) { 169 T* typed_ptr = static_cast<T*>(ptr); 170 for (const auto i : c10::irange(n)) { 171 new (typed_ptr + i) T; 172 } 173 } 174 175 template <typename T> 176 inline void _PlacementNewNotDefault(void* /*ptr*/, size_t /*n*/) { 177 _ThrowRuntimeTypeLogicError( 178 "Type " + std::string(c10::util::get_fully_qualified_type_name<T>()) + 179 " is not default-constructible."); 180 } 181 182 template < 183 typename T, 184 std::enable_if_t<std::is_default_constructible_v<T>>* = nullptr> 185 inline constexpr TypeMetaData::PlacementNew* _PickPlacementNew() { 186 return (c10::guts::is_fundamental<T>::value || std::is_pointer_v<T>) 187 ? nullptr 188 : &_PlacementNew<T>; 189 } 190 191 template < 192 typename T, 193 std::enable_if_t<!std::is_default_constructible_v<T>>* = nullptr> 194 inline constexpr TypeMetaData::PlacementNew* _PickPlacementNew() { 195 static_assert( 196 !c10::guts::is_fundamental<T>::value && !std::is_pointer_v<T>, 197 "this should have picked the other SFINAE case"); 198 return &_PlacementNewNotDefault<T>; 199 } 200 201 template <typename T> 202 inline void* _New() { 203 return new T; 204 } 205 206 template <typename T> 207 inline void* _NewNotDefault() { 208 _ThrowRuntimeTypeLogicError( 209 "Type " + std::string(c10::util::get_fully_qualified_type_name<T>()) + 210 " is not default-constructible."); 211 } 212 213 template < 214 typename T, 215 std::enable_if_t<std::is_default_constructible_v<T>>* = nullptr> 216 inline constexpr TypeMetaData::New* _PickNew() { 217 return &_New<T>; 218 } 219 220 template < 221 typename T, 222 std::enable_if_t<!std::is_default_constructible_v<T>>* = nullptr> 223 inline constexpr TypeMetaData::New* _PickNew() { 224 return &_NewNotDefault<T>; 225 } 226 227 /** 228 * Typed copy function for classes. 229 */ 230 template <typename T> 231 inline void _Copy(const void* src, void* dst, size_t n) { 232 const T* typed_src = static_cast<const T*>(src); 233 T* typed_dst = static_cast<T*>(dst); 234 for (const auto i : c10::irange(n)) { 235 typed_dst[i] = typed_src[i]; 236 } 237 } 238 239 /** 240 * A placeholder function for types that do not allow assignment. 241 */ 242 template <typename T> 243 inline void _CopyNotAllowed(const void* /*src*/, void* /*dst*/, size_t /*n*/) { 244 _ThrowRuntimeTypeLogicError( 245 "Type " + std::string(c10::util::get_fully_qualified_type_name<T>()) + 246 " does not allow assignment."); 247 } 248 249 template <typename T, std::enable_if_t<std::is_copy_assignable_v<T>>* = nullptr> 250 inline constexpr TypeMetaData::Copy* _PickCopy() { 251 return (c10::guts::is_fundamental<T>::value || std::is_pointer_v<T>) 252 ? nullptr 253 : &_Copy<T>; 254 } 255 256 template < 257 typename T, 258 std::enable_if_t<!std::is_copy_assignable_v<T>>* = nullptr> 259 inline constexpr TypeMetaData::Copy* _PickCopy() { 260 static_assert( 261 !c10::guts::is_fundamental<T>::value && !std::is_pointer_v<T>, 262 "this should have picked the other SFINAE case"); 263 return &_CopyNotAllowed<T>; 264 } 265 266 /** 267 * Destructor for non-fundamental types. 268 */ 269 template <typename T> 270 inline void _PlacementDelete(void* ptr, size_t n) { 271 T* typed_ptr = static_cast<T*>(ptr); 272 for (const auto i : c10::irange(n)) { 273 typed_ptr[i].~T(); 274 } 275 } 276 277 template <typename T> 278 inline constexpr TypeMetaData::PlacementDelete* _PickPlacementDelete() { 279 return (c10::guts::is_fundamental<T>::value || std::is_pointer_v<T>) 280 ? nullptr 281 : &_PlacementDelete<T>; 282 } 283 284 template <typename T> 285 inline void _Delete(void* ptr) { 286 T* typed_ptr = static_cast<T*>(ptr); 287 delete typed_ptr; 288 } 289 290 template <class T> 291 inline constexpr TypeMetaData::Delete* _PickDelete() noexcept { 292 return &_Delete<T>; 293 } 294 295 class _Uninitialized final {}; 296 297 } // namespace detail 298 299 // 300 // note: this is outside TypeMeta bc gcc seems to have trouble 301 // with scalarTypeItemSizes as a constexpr static member used by 302 // a public inline instance method 303 // 304 305 // item sizes for TypeMeta::itemsize() fast path 306 static constexpr std::array<uint8_t, NumScalarTypes> scalarTypeItemSizes = { 307 #define SCALAR_TYPE_SIZE(T, name) sizeof(T), 308 AT_FORALL_SCALAR_TYPES_WITH_COMPLEX_AND_QINTS(SCALAR_TYPE_SIZE) 309 #undef SCALAR_TYPE_SIZE 310 0, // Undefined 311 }; 312 313 /** 314 * TypeMeta is a thin class that allows us to store the type of a container such 315 * as a blob, or the data type of a tensor, with a unique run-time id. It also 316 * stores some additional data such as the item size and the name of the type 317 * for run-time inspection. 318 */ 319 class C10_API TypeMeta final { 320 public: 321 using New = detail::TypeMetaData::New; 322 using PlacementNew = detail::TypeMetaData::PlacementNew; 323 using Copy = detail::TypeMetaData::Copy; 324 using PlacementDelete = detail::TypeMetaData::PlacementDelete; 325 using Delete = detail::TypeMetaData::Delete; 326 327 /** Create a dummy TypeMeta object. To create a TypeMeta object for a specific 328 * type, use TypeMeta::Make<T>(). 329 */ 330 TypeMeta() noexcept; 331 332 /** 333 * Copy constructor. 334 */ 335 TypeMeta(const TypeMeta& src) noexcept = default; 336 337 /** 338 * Assignment operators. 339 */ 340 TypeMeta& operator=(const TypeMeta& src) noexcept = default; 341 342 TypeMeta(TypeMeta&& rhs) noexcept = default; 343 344 inline TypeMeta& operator=(ScalarType scalar_type) noexcept { 345 index_ = static_cast<uint16_t>(scalar_type); 346 return *this; 347 } 348 349 private: 350 // TypeMeta can only be created by Make, making sure that we do not 351 // create incorrectly mixed up TypeMeta objects. 352 explicit TypeMeta(const uint16_t index) noexcept : index_(index) {} 353 354 public: 355 /** 356 * Returns the type id. 357 */ 358 TypeIdentifier id() const noexcept { 359 return data().id_; 360 } 361 /** 362 * true if we represent some ScalarType type 363 */ 364 inline bool isScalarType() const noexcept { 365 return index_ < NumScalarTypes; 366 } 367 /** 368 * true if we represent ScalarType scalar_type 369 */ 370 inline bool isScalarType(ScalarType scalar_type) const noexcept { 371 return index_ == static_cast<uint16_t>(scalar_type); 372 } 373 /** 374 * Returns the size of the item. 375 */ 376 inline size_t itemsize() const noexcept { 377 if (C10_LIKELY(isScalarType())) { 378 return scalarTypeItemSizes[index_]; 379 } 380 return data().itemsize_; 381 } 382 /** 383 * Returns the new function pointer for individual items. 384 */ 385 New* newFn() const noexcept { 386 return data().new_; 387 } 388 /** 389 * Returns the placement new function pointer for individual items. 390 */ 391 PlacementNew* placementNew() const noexcept { 392 return data().placementNew_; 393 } 394 /** 395 * Returns the typed copy function pointer for individual iterms. 396 */ 397 Copy* copy() const noexcept { 398 return data().copy_; 399 } 400 /** 401 * Returns the destructor function pointer for individual items. 402 */ 403 PlacementDelete* placementDelete() const noexcept { 404 return data().placementDelete_; 405 } 406 Delete* deleteFn() const noexcept { 407 return data().delete_; 408 } 409 /** 410 * Returns a printable name for the type. 411 */ 412 c10::string_view name() const noexcept { 413 return data().name_; 414 } 415 416 friend bool operator==(const TypeMeta& lhs, const TypeMeta& rhs) noexcept; 417 418 template <typename T> 419 bool Match() const noexcept { 420 return (*this == Make<T>()); 421 } 422 423 // Below are static functions that can be called by passing a specific type. 424 425 template <class T> 426 static C10_HOST_CONSTEXPR_EXCEPT_WIN_CUDA TypeIdentifier Id() noexcept { 427 return TypeIdentifier::Get<T>(); 428 } 429 430 template <class T> 431 static c10::string_view TypeName() noexcept { 432 return c10::util::get_fully_qualified_type_name<T>(); 433 } 434 435 template <class T> 436 static constexpr size_t ItemSize() noexcept { 437 return sizeof(T); 438 } 439 440 /** 441 * Returns a TypeMeta object that corresponds to the typename T. 442 */ 443 template <typename T> 444 static TypeMeta Make() { 445 // The instance pointed to is declared here, but defined in a .cpp file. 446 // We need to silence the compiler warning about using an undefined 447 // variable template. '-Wpragmas' and '-Wunknown-warning-option' has to be 448 // disabled for compilers that don't know '-Wundefined-var-template' and 449 // would error at our attempt to disable it. 450 #ifndef _MSC_VER 451 #pragma GCC diagnostic push 452 #pragma GCC diagnostic ignored "-Wpragmas" 453 #pragma GCC diagnostic ignored "-Wunknown-warning-option" 454 #pragma GCC diagnostic ignored "-Wundefined-var-template" 455 #endif 456 return TypeMeta(_typeMetaData<T>()); 457 #ifndef _MSC_VER 458 #pragma GCC diagnostic pop 459 #endif 460 } 461 462 /** 463 * convert ScalarType enum values to TypeMeta handles 464 */ 465 static inline caffe2::TypeMeta fromScalarType(ScalarType scalar_type) { 466 const auto index = static_cast<uint16_t>(scalar_type); 467 TORCH_INTERNAL_ASSERT_DEBUG_ONLY( 468 index < NumScalarTypes, 469 "Unrecognized Scalartype ", 470 scalar_type, 471 " (please report this error)"); 472 return TypeMeta(index); 473 } 474 475 /** 476 * convert TypeMeta handles to ScalarType enum values 477 */ 478 inline ScalarType toScalarType() { 479 if (C10_LIKELY(isScalarType())) { 480 return static_cast<ScalarType>(index_); 481 } 482 error_unsupported_typemeta(*this); 483 } 484 485 private: 486 [[noreturn]] static void error_unsupported_typemeta(caffe2::TypeMeta dtype); 487 488 // hard limit number of registered types 489 // note: constexpr provokes Windows compilation error "member may not be 490 // initialized" static constexpr size_t MaxTypeIndex = 32; 491 // 492 #if defined C10_MOBILE 493 // The reason for this not to be UINT8_MAX is that the array 494 // initialization takes space which is proportional to the size of the array. 495 // The compiler seems to add code (or data padding) to initialize the array with 496 // empty elements. Please see 497 // https://github.com/pytorch/pytorch/pull/51881 for details. 498 // 499 #define MaxTypeIndex \ 500 (NumScalarTypes + 15 /* number of CAFFE_DEFINE_KNOWN_TYPE in typeid.cpp */ + \ 501 1 /* 1 more for caffe2 tensor */) 502 #else 503 #define MaxTypeIndex UINT8_MAX 504 #endif 505 506 // Protects type metadata allocation. 507 // NOLINTNEXTLINE(facebook-hte-NonPodStaticDeclaration) 508 static std::mutex& getTypeMetaDatasLock(); 509 static uint16_t nextTypeIndex; 510 511 static detail::TypeMetaData* typeMetaDatas(); 512 513 static uint16_t existingMetaDataIndexForType(TypeIdentifier identifier); 514 515 public: 516 #ifdef __CUDACC__ 517 // NOTE [ TypeIdentifier::Get nvcc/clang discrepancy] 518 // nvcc and clang do not produce identical results for 519 // TypeIdentifier::Get, because TypeIdentifier::Get relies on 520 // __PRETTY_FUNCTION__ and they don't agree on the canonical names 521 // of types (e.g., nvcc normalizes to `short unsigned int`, but clang 522 // calls it `unsigned short`). Hide the implementation of this function 523 // from nvcc so that we always use clang (or whatever host C++ compiler) 524 // for TypeIdentifier::Get. 525 template <class T> 526 C10_EXPORT static uint16_t addTypeMetaData(); 527 #else 528 template <class T> 529 C10_EXPORT static uint16_t addTypeMetaData() { 530 const auto identifier = TypeIdentifier::Get<T>(); 531 // Need to hold this for the rest of the function, protecting: 532 // 1) existingMetaDataIndexForType() 533 // 2) nextTypeIndex++ 534 // 3) the write into typeMetaDatas() 535 std::lock_guard<std::mutex> lock(getTypeMetaDatasLock()); 536 // It may exist already if added in a different dynamic shared library. 537 const uint16_t existing_index = existingMetaDataIndexForType(identifier); 538 if (existing_index != MaxTypeIndex) { 539 return existing_index; 540 } 541 const uint16_t index = nextTypeIndex++; 542 TORCH_CHECK( 543 index <= MaxTypeIndex, 544 "Maximum number of CAFFE_KNOWN_TYPE declarations has been exceeded. ", 545 "Please report this issue."); 546 typeMetaDatas()[index] = detail::TypeMetaData{ 547 sizeof(T), 548 detail::_PickNew<T>(), 549 detail::_PickPlacementNew<T>(), 550 detail::_PickCopy<T>(), 551 detail::_PickPlacementDelete<T>(), 552 detail::_PickDelete<T>(), 553 identifier, 554 c10::util::get_fully_qualified_type_name<T>()}; 555 return index; 556 } 557 #endif 558 559 private: 560 // specializations return indexes into typeMetaDataInstances() 561 template <class T> 562 C10_API static uint16_t _typeMetaData() noexcept; 563 564 // 565 // TypeMeta just wraps this index 566 // 567 568 uint16_t index_; 569 570 inline const detail::TypeMetaData& data() const { 571 return typeMetaDatas()[index_]; 572 } 573 }; 574 575 // specializations of TypeMeta::_typeMetaData for ScalarType types 576 577 #define DEFINE_SCALAR_METADATA_INSTANCE(T, name) \ 578 template <> \ 579 constexpr uint16_t TypeMeta::_typeMetaData<T>() noexcept { \ 580 return static_cast<uint16_t>(ScalarType::name); \ 581 } 582 AT_FORALL_SCALAR_TYPES_WITH_COMPLEX_AND_QINTS(DEFINE_SCALAR_METADATA_INSTANCE) 583 #undef DEFINE_SCALAR_METADATA_INSTANCE 584 585 template <> 586 C10_EXPORT constexpr uint16_t TypeMeta::_typeMetaData< 587 detail::_Uninitialized>() noexcept { 588 return static_cast<uint16_t>(ScalarType::Undefined); 589 } 590 591 inline TypeMeta::TypeMeta() noexcept 592 : index_(_typeMetaData<detail::_Uninitialized>()) {} 593 594 inline bool operator==(const TypeMeta& lhs, const TypeMeta& rhs) noexcept { 595 return (lhs.index_ == rhs.index_); 596 } 597 inline bool operator!=(const TypeMeta& lhs, const TypeMeta& rhs) noexcept { 598 return !operator==(lhs, rhs); 599 } 600 601 inline std::ostream& operator<<( 602 std::ostream& stream, 603 caffe2::TypeMeta typeMeta) { 604 return stream << typeMeta.name(); 605 } 606 607 /** 608 * Register unique id for a type so it can be used in TypeMeta context, e.g. be 609 * used as a type for Blob or for Tensor elements. 610 * 611 * CAFFE_KNOWN_TYPE is deprecated; prefer CAFFE_DECLARE_KNOWN_TYPE and 612 * CAFFE_DEFINE_KNOWN_TYPE. 613 * 614 * CAFFE_KNOWN_TYPE does explicit instantiation of TypeIdentifier::Get<T> 615 * template function and thus needs to be put in a single translation unit (.cpp 616 * file) for a given type T. Other translation units that use type T as a type 617 * of the caffe2::Blob or element type of caffe2::Tensor need to depend on the 618 * translation unit that contains CAFFE_KNOWN_TYPE declaration via regular 619 * linkage dependencies. 620 * 621 * NOTE: the macro needs to be invoked in ::caffe2 namespace 622 */ 623 // Implementation note: in MSVC, we will need to prepend the C10_API 624 // keyword in order to get things compiled properly. in Linux, gcc seems to 625 // create attribute ignored error for explicit template instantiations, see 626 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0537r0.html 627 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51930 628 // and as a result, we define these two macros slightly differently. 629 #if defined(_MSC_VER) || defined(__clang__) 630 #define EXPORT_IF_NOT_GCC C10_EXPORT 631 #else 632 #define EXPORT_IF_NOT_GCC 633 #endif 634 635 // CAFFE_KNOWN_TYPE is deprecated! Use CAFFE_DECLARE_KNOWN_TYPE and 636 // CAFFE_DEFINE_KNOWN_TYPE instead. 637 #define CAFFE_KNOWN_TYPE(T) \ 638 template uint16_t TypeMeta::addTypeMetaData<T>(); \ 639 template <> \ 640 EXPORT_IF_NOT_GCC uint16_t TypeMeta::_typeMetaData<T>() noexcept { \ 641 static const uint16_t index = addTypeMetaData<T>(); \ 642 return index; \ 643 } 644 645 #define CAFFE_DEFINE_KNOWN_TYPE(T, ident) \ 646 template uint16_t TypeMeta::addTypeMetaData<T>(); \ 647 namespace detail { \ 648 EXPORT_IF_NOT_GCC const uint16_t ident##_metadata_index = \ 649 TypeMeta::addTypeMetaData<T>(); \ 650 } // namespace detail 651 652 // Unlike CAFFE_KNOWN_TYPE, CAFFE_DECLARE_KNOWN_TYPE avoids a function 653 // call to access _typeMetaData in the common case. 654 #define CAFFE_DECLARE_KNOWN_TYPE(T, ident) \ 655 extern template uint16_t TypeMeta::addTypeMetaData<T>(); \ 656 namespace detail { \ 657 extern C10_API const uint16_t ident##_metadata_index; \ 658 } /* namespace detail */ \ 659 template <> \ 660 EXPORT_IF_NOT_GCC C10_ALWAYS_INLINE uint16_t \ 661 TypeMeta::_typeMetaData<T>() noexcept { \ 662 return detail::ident##_metadata_index; \ 663 } 664 665 #define CAFFE_KNOWN_TYPE_NOEXPORT(T) \ 666 template <> \ 667 uint16_t TypeMeta::_typeMetaData<T>() noexcept { \ 668 static const uint16_t index = addTypeMetaData<T>(); \ 669 return index; \ 670 } 671 672 CAFFE_DECLARE_KNOWN_TYPE(std::string, std_string) 673 CAFFE_DECLARE_KNOWN_TYPE(char, char) 674 CAFFE_DECLARE_KNOWN_TYPE(std::unique_ptr<std::mutex>, std_unique_ptr_std_mutex) 675 CAFFE_DECLARE_KNOWN_TYPE( 676 std::unique_ptr<std::atomic<bool>>, 677 std_unique_ptr_std_atomic_bool) 678 CAFFE_DECLARE_KNOWN_TYPE(std::vector<int32_t>, std_vector_int32_t) 679 CAFFE_DECLARE_KNOWN_TYPE(std::vector<int64_t>, std_vector_int64_t) 680 CAFFE_DECLARE_KNOWN_TYPE(std::vector<unsigned long>, std_vector_unsigned_long) 681 CAFFE_DECLARE_KNOWN_TYPE(bool*, bool_ptr) 682 CAFFE_DECLARE_KNOWN_TYPE(char*, char_ptr) 683 CAFFE_DECLARE_KNOWN_TYPE(int*, int_ptr) 684 685 // For some of the compilers, long is defined separately from int32_t and 686 // int64_t. As a result we will need to actually define them separately. 687 // It is recommended that one does NOT use long - use int32_t and int64_t 688 // explicitly. Explicit long type annotation may go away in the future. 689 // details: This hack works by defining a _guard_long_unique type, which is 690 // long iff the compiler has a separate long type and is a dummy type otherwise. 691 // we then allocate a type id to that _guard_long_unique. If the compiler has a 692 // separate long type, this allocates a type id for long. Otherwise, it 693 // allocates a type id for the dummy type, which doesn't matter. 694 namespace detail { 695 template <class T> 696 class _guard_long_unique_dummy final {}; 697 template <class T> 698 using _guard_long_unique = std::conditional_t< 699 std::is_same_v<long, int32_t> || std::is_same_v<long, int64_t>, 700 _guard_long_unique_dummy<T>, 701 T>; 702 } // namespace detail 703 704 CAFFE_DECLARE_KNOWN_TYPE( 705 detail::_guard_long_unique<long>, 706 detail_guard_long_unique_long); 707 CAFFE_DECLARE_KNOWN_TYPE( 708 detail::_guard_long_unique<std::vector<long>>, 709 detail_guard_long_unique_std_vector_long) 710 711 CAFFE_DECLARE_KNOWN_TYPE(float*, float_ptr) 712 CAFFE_DECLARE_KNOWN_TYPE(at::Half*, at_Half) 713 714 } // namespace caffe2 715