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