xref: /aosp_15_r20/external/pytorch/c10/util/typeid.h (revision da0073e96a02ea20f0ac840b70461e3646d07c45)
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