Caffe2 - C++ API
A deep learning, cross platform ML framework
typeid.h
1 #pragma once
2 
3 #include <atomic>
4 #include <cassert>
5 #include <cstdlib>
6 #include <iostream>
7 #include <memory>
8 #include <mutex>
9 #include <type_traits>
10 #include <unordered_map>
11 #include <unordered_set>
12 #include <vector>
13 #include <complex>
14 #ifdef __GXX_RTTI
15 #include <typeinfo>
16 #endif
17 
18 #include <exception>
19 
20 #include "c10/util/Backtrace.h"
21 #include "c10/util/Half.h"
22 #include "c10/macros/Macros.h"
23 #include "c10/util/C++17.h"
24 #include "c10/util/Exception.h"
25 #include "c10/util/IdWrapper.h"
26 
27 #include "c10/util/Type.h"
28 
29 /*
30  * TypeIdentifier is a small type containing an id.
31  * Types must be registered using CAFFE_KNOWN_TYPE() for them to have a type id.
32  * If a type is registered, you can also create an object containing meta data
33  * like constructor, destructor, stringified name, ... about the type by calling
34  * TypeMeta::Make<T>. This returns a TypeMeta() object, which is basically just
35  * a pointer to the type information, so it's cheap to pass around.
36  */
37 
38 // TODO: This file is still in the caffe2 namespace, despite living
39 // in the ATen directory. This is because the macro
40 // CAFFE_KNOWN_TYPE defines a template 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 namespace std {
47 template<>
48 struct is_fundamental<at::Half> : std::true_type {
49 };
50 } // namespace std
51 
52 namespace caffe2 {
53 
60 class C10_API TypeIdentifier final
61  : public at::IdWrapper<TypeIdentifier, uint16_t> {
62  public:
63  static TypeIdentifier createTypeId();
64 
65  friend std::ostream& operator<<(
66  std::ostream& stream,
67  TypeIdentifier typeId);
68  friend bool operator<(TypeIdentifier lhs, TypeIdentifier rhs);
69 
70  // 0 is uint8_t (due to ScalarType BC constraint)
71  static constexpr TypeIdentifier uninitialized() {
72  return TypeIdentifier(11);
73  }
74 
82  template <typename T>
83  C10_API static TypeIdentifier Get();
84 
85  private:
86  constexpr explicit TypeIdentifier(uint16_t id) : IdWrapper(id) {}
87  friend class TypeMeta;
88 };
89 
90 // Allow usage in std::map / std::set
91 // TODO Disallow this and rather use std::unordered_map/set everywhere
92 inline bool operator<(TypeIdentifier lhs, TypeIdentifier rhs) {
93  return lhs.underlyingId() < rhs.underlyingId();
94 }
95 
96 inline std::ostream& operator<<(
97  std::ostream& stream,
98  caffe2::TypeIdentifier typeId) {
99  return stream << typeId.underlyingId();
100 }
101 
102 } // namespace caffe2
103 
104 namespace at {
105 using DataType = caffe2::TypeIdentifier;
106 }
107 
108 C10_DEFINE_HASH_FOR_IDWRAPPER(caffe2::TypeIdentifier)
109 
110 namespace caffe2 {
111 
112 namespace detail {
113 
114 // This struct holds the actual type information. There will be
115 // one allocated per type. TypeMeta objects will then point to the struct
116 // instance for the type they're configured for.
117 struct TypeMetaData final {
118  using New = void*();
119  using PlacementNew = void(void*, size_t);
120  using Copy = void(const void*, void*, size_t);
121  using PlacementDelete = void(void*, size_t);
122  using Delete = void(void*);
123 
124  TypeMetaData() = delete;
125  constexpr TypeMetaData(
126  size_t itemsize,
127  New* newFn,
128  PlacementNew* placementNew,
129  Copy* copy,
130  PlacementDelete* placementDelete,
131  Delete* deleteFn,
132  TypeIdentifier id,
133  const char* name) noexcept
134  : itemsize_(itemsize), new_(newFn), placementNew_(placementNew), copy_(copy), placementDelete_(placementDelete), delete_(deleteFn), id_(id), name_(name) {}
135 
136  size_t itemsize_;
137  New* new_;
138  PlacementNew* placementNew_;
139  Copy* copy_;
140  PlacementDelete* placementDelete_;
141  Delete* delete_;
142  TypeIdentifier id_;
143  const char* name_;
144 };
145 
146 // Mechanism for throwing errors which can't be prevented at compile time
147 // due to type erasure. E.g. somebody calling TypeMeta::copy() for
148 // non-copyable type. Right now just throws exception but is implemented
149 // in .cpp to manage dependencies
150 [[noreturn]] C10_API void _ThrowRuntimeTypeLogicError(const std::string& msg);
151 
155 template <typename T>
156 inline void _PlacementNew(void* ptr, size_t n) {
157  T* typed_ptr = static_cast<T*>(ptr);
158  for (size_t i = 0; i < n; ++i) {
159  new (typed_ptr + i) T;
160  }
161 }
162 
163 template <typename T>
164 inline void _PlacementNewNotDefault(void* /*ptr*/, size_t /*n*/) {
165  _ThrowRuntimeTypeLogicError(
166  "Type " + std::string(c10::demangle_type<T>()) +
167  " is not default-constructible.");
168 }
169 
170 template<
171  typename T,
172  c10::guts::enable_if_t<std::is_default_constructible<T>::value>* = nullptr>
173 inline constexpr TypeMetaData::PlacementNew* _PickPlacementNew() {
174  return
175  (std::is_fundamental<T>::value || std::is_pointer<T>::value)
176  ? nullptr
177  : &_PlacementNew<T>;
178 }
179 
180 template<
181  typename T,
182  c10::guts::enable_if_t<!std::is_default_constructible<T>::value>* = nullptr>
183 inline constexpr TypeMetaData::PlacementNew* _PickPlacementNew() {
184  static_assert(!std::is_fundamental<T>::value && !std::is_pointer<T>::value, "this should have picked the other SFINAE case");
185  return &_PlacementNewNotDefault<T>;
186 }
187 
188 template <typename T>
189 inline void* _New() {
190  return new T;
191 }
192 
193 template <typename T>
194 inline void* _NewNotDefault() {
195  _ThrowRuntimeTypeLogicError(
196  "Type " + std::string(c10::demangle_type<T>()) +
197  " is not default-constructible.");
198 }
199 
200 template<
201  typename T,
202  c10::guts::enable_if_t<std::is_default_constructible<T>::value>* = nullptr>
203 inline constexpr TypeMetaData::New* _PickNew() {
204  return &_New<T>;
205 }
206 
207 template <
208  typename T,
209  c10::guts::enable_if_t<!std::is_default_constructible<T>::value>* = nullptr>
210 inline constexpr TypeMetaData::New* _PickNew() {
211  return &_NewNotDefault<T>;
212 }
213 
217 template <typename T>
218 inline void _Copy(const void* src, void* dst, size_t n) {
219  const T* typed_src = static_cast<const T*>(src);
220  T* typed_dst = static_cast<T*>(dst);
221  for (size_t i = 0; i < n; ++i) {
222  typed_dst[i] = typed_src[i];
223  }
224 }
225 
229 template <typename T>
230 inline void _CopyNotAllowed(const void* /*src*/, void* /*dst*/, size_t /*n*/) {
231  _ThrowRuntimeTypeLogicError(
232  "Type " + std::string(c10::demangle_type<T>()) +
233  " does not allow assignment.");
234 }
235 
236 template<
237  typename T,
238  c10::guts::enable_if_t<std::is_copy_assignable<T>::value>* = nullptr
239  >
240 inline constexpr TypeMetaData::Copy* _PickCopy() {
241  return
242  (std::is_fundamental<T>::value || std::is_pointer<T>::value)
243  ? nullptr
244  : &_Copy<T>;
245 }
246 
247 template<
248  typename T,
249  c10::guts::enable_if_t<!std::is_copy_assignable<T>::value>* = nullptr
250  >
251 inline constexpr TypeMetaData::Copy* _PickCopy() {
252  static_assert(!std::is_fundamental<T>::value && !std::is_pointer<T>::value, "this should have picked the other SFINAE case");
253  return &_CopyNotAllowed<T>;
254 }
255 
259 template <typename T>
260 inline void _PlacementDelete(void* ptr, size_t n) {
261  T* typed_ptr = static_cast<T*>(ptr);
262  for (size_t i = 0; i < n; ++i) {
263  typed_ptr[i].~T();
264  }
265 }
266 
267 template <typename T>
268 inline constexpr TypeMetaData::PlacementDelete* _PickPlacementDelete() {
269  return
270  (std::is_fundamental<T>::value || std::is_pointer<T>::value)
271  ? nullptr
272  : &_PlacementDelete<T>;
273 }
274 
275 template <typename T>
276 inline void _Delete(void* ptr) {
277  T* typed_ptr = static_cast<T*>(ptr);
278  delete typed_ptr;
279 }
280 
281 template<class T>
282 inline constexpr TypeMetaData::Delete* _PickDelete() noexcept {
283  return &_Delete<T>;
284 }
285 
286 #ifdef __GXX_RTTI
287 template <class T>
288 const char* _typeName(const char* literalName) noexcept {
289  std::ignore = literalName; // suppress unused warning
290  static const std::string name = c10::demangle(typeid(T).name());
291  return name.c_str();
292 }
293 #else
294 template <class T>
295 constexpr const char* _typeName(const char* literalName) noexcept {
296  return literalName;
297 }
298 #endif
299 
300 template<class T>
301 inline TypeMetaData _makeTypeMetaDataInstance(const char* typeName) {
302  return {
303  sizeof(T),
304  _PickNew<T>(),
305  _PickPlacementNew<T>(),
306  _PickCopy<T>(),
307  _PickPlacementDelete<T>(),
308  _PickDelete<T>(),
309  TypeIdentifier::Get<T>(),
310  typeName
311  };
312 }
313 
314 class _Uninitialized final {};
315 
316 } // namespace detail
317 
324 class C10_API TypeMeta {
325  public:
326  using New = detail::TypeMetaData::New;
327  using PlacementNew = detail::TypeMetaData::PlacementNew;
328  using Copy = detail::TypeMetaData::Copy;
329  using PlacementDelete = detail::TypeMetaData::PlacementDelete;
330  using Delete = detail::TypeMetaData::Delete;
331 
335  TypeMeta() noexcept;
336 
340  constexpr TypeMeta(const TypeMeta& src) noexcept = default;
341 
345  AT_CPP14_CONSTEXPR TypeMeta& operator=(const TypeMeta& src) noexcept =
346  default;
347 
348  constexpr TypeMeta(TypeMeta&& rhs) noexcept = default;
349 
350  private:
351  // TypeMeta can only be created by Make, making sure that we do not
352  // create incorrectly mixed up TypeMeta objects.
353  explicit constexpr TypeMeta(const detail::TypeMetaData* data) noexcept : data_(data) {}
354 
355  public:
359  constexpr TypeIdentifier id() const noexcept {
360  return data_->id_;
361  }
365  constexpr size_t itemsize() const noexcept {
366  return data_->itemsize_;
367  }
368  constexpr New* newFn() const noexcept {
369  return data_->new_;
370  }
374  constexpr PlacementNew* placementNew() const noexcept {
375  return data_->placementNew_;
376  }
380  constexpr Copy* copy() const noexcept {
381  return data_->copy_;
382  }
386  constexpr PlacementDelete* placementDelete() const noexcept {
387  return data_->placementDelete_;
388  }
389  constexpr Delete* deleteFn() const noexcept {
390  return data_->delete_;
391  }
395  constexpr const char* name() const noexcept {
396  return data_->name_;
397  }
398 
399  friend bool operator==(const TypeMeta& lhs, const TypeMeta& rhs) noexcept;
400 
401  template <typename T>
402  constexpr bool Match() const noexcept {
403  return (*this == Make<T>());
404  }
405 
406  // Below are static functions that can be called by passing a specific type.
407 
408  template <class T>
409  static TypeIdentifier Id() noexcept {
410  return TypeIdentifier::Get<T>();
411  }
412 
413  template <class T>
414  static const char* TypeName() noexcept {
415  return Make<T>().name();
416  }
417 
418  template <class T>
419  static constexpr size_t ItemSize() noexcept {
420  return sizeof(T);
421  }
422 
426  template <typename T>
427  static TypeMeta Make() {
428  // The instance pointed to is declared here, but defined in a .cpp file.
429  // We need to silence the compiler warning about using an undefined
430  // variable template. '-Wpragmas' and '-Wunknown-warning-option' has to be
431  // disabled for compilers that don't know '-Wundefined-var-template' and
432  // would error at our attempt to disable it.
433 #ifndef _MSC_VER
434 # pragma GCC diagnostic push
435 # pragma GCC diagnostic ignored "-Wpragmas"
436 # pragma GCC diagnostic ignored "-Wunknown-warning-option"
437 # pragma GCC diagnostic ignored "-Wundefined-var-template"
438 #endif
439  return TypeMeta(_typeMetaDataInstance<T>());
440 #ifndef _MSC_VER
441 # pragma GCC diagnostic pop
442 #endif
443  }
444 
445  private:
446  const detail::TypeMetaData* data_;
447 
448  template<class T>
449  C10_API static const detail::TypeMetaData* _typeMetaDataInstance() noexcept;
450 };
451 
452 template<>
453 C10_EXPORT const detail::TypeMetaData* TypeMeta::_typeMetaDataInstance<detail::_Uninitialized>() noexcept;
454 
455 inline TypeMeta::TypeMeta() noexcept : data_(_typeMetaDataInstance<detail::_Uninitialized>()) {}
456 
457 inline bool operator==(const TypeMeta& lhs, const TypeMeta& rhs) noexcept {
458  return (lhs.data_ == rhs.data_);
459 }
460 inline bool operator!=(const TypeMeta& lhs, const TypeMeta& rhs) noexcept {
461  return !operator==(lhs, rhs);
462 }
463 
464 inline std::ostream& operator<<(
465  std::ostream& stream,
466  caffe2::TypeMeta typeMeta) {
467  return stream << typeMeta.name();
468 }
469 
483 // Implementation note: in MSVC, we will need to prepend the C10_API
484 // keyword in order to get things compiled properly. in Linux, gcc seems to
485 // create attribute ignored error for explicit template instantiations, see
486 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0537r0.html
487 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51930
488 // and as a result, we define these two macros slightly differently.
489 #if defined(_MSC_VER) || defined(__clang__)
490 #define EXPORT_IF_NOT_GCC C10_EXPORT
491 #else
492 #define EXPORT_IF_NOT_GCC
493 #endif
494 
495 #define _CAFFE_KNOWN_TYPE_DEFINE_TYPEMETADATA_INSTANCE(T, Counter) \
496  namespace detail { \
497  const TypeMetaData MACRO_CONCAT(_typeMetaDataInstance_, Counter) = \
498  _makeTypeMetaDataInstance<T>(_typeName<T>(#T)); \
499  } \
500  template<> \
501  EXPORT_IF_NOT_GCC const detail::TypeMetaData* TypeMeta::_typeMetaDataInstance<T>() noexcept { \
502  return &MACRO_CONCAT(detail::_typeMetaDataInstance_, Counter); \
503  }
504 #define CAFFE_KNOWN_TYPE(T) \
505  template <> \
506  EXPORT_IF_NOT_GCC TypeIdentifier TypeIdentifier::Get<T>() { \
507  static const TypeIdentifier type_id = TypeIdentifier::createTypeId(); \
508  return type_id; \
509  } \
510  _CAFFE_KNOWN_TYPE_DEFINE_TYPEMETADATA_INSTANCE(T, __COUNTER__)
511 
518 #ifdef _MSC_VER
519 #define CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(PreallocatedId, T) \
520  template <> \
521  inline C10_EXPORT TypeIdentifier TypeIdentifier::Get<T>() { \
522  return TypeIdentifier(PreallocatedId); \
523  } \
524  namespace detail { \
525  C10_API extern const TypeMetaData \
526  MACRO_CONCAT(_typeMetaDataInstance_preallocated_, PreallocatedId); \
527  }
528 #define CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(PreallocatedId, T) \
529  namespace detail { \
530  C10_EXPORT const TypeMetaData \
531  MACRO_CONCAT(_typeMetaDataInstance_preallocated_, PreallocatedId) \
532  = _makeTypeMetaDataInstance<T>(_typeName<T>(#T)); \
533  } \
534  template<> \
535  C10_EXPORT const detail::TypeMetaData* TypeMeta::_typeMetaDataInstance<T>() noexcept { \
536  return &MACRO_CONCAT(detail::_typeMetaDataInstance_preallocated_, PreallocatedId); \
537  }
538 #else // _MSC_VER
539 #define CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(PreallocatedId, T) \
540  template <> \
541  inline C10_EXPORT TypeIdentifier TypeIdentifier::Get<T>() { \
542  return TypeIdentifier(PreallocatedId); \
543  } \
544  namespace detail { \
545  C10_EXPORT extern const TypeMetaData \
546  MACRO_CONCAT(_typeMetaDataInstance_preallocated_, PreallocatedId); \
547  } \
548  template<> \
549  inline const detail::TypeMetaData* TypeMeta::_typeMetaDataInstance<T>() noexcept { \
550  return &MACRO_CONCAT(detail::_typeMetaDataInstance_preallocated_, PreallocatedId); \
551  }
552 #define CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(PreallocatedId, T) \
553  namespace detail { \
554  const TypeMetaData MACRO_CONCAT(_typeMetaDataInstance_preallocated_, PreallocatedId) \
555  = _makeTypeMetaDataInstance<T>(_typeName<T>(#T)); \
556  }
557 #endif
558 
559 // Note: we have preallocated the numbers so they line up exactly
560 // with at::ScalarType's numbering. All other numbers do not matter.
561 
563 // TODO static_assert number of declare/define align
564 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(0, uint8_t)
565 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(1, int8_t)
566 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(2, int16_t)
567 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(3, int)
568 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(4, int64_t)
569 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(5, at::Half)
570 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(6, float)
571 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(7, double)
572 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(8, at::ComplexHalf)
573 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(9, std::complex<float>)
574 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(10, std::complex<double>)
575 // 11 = undefined type id
576 // 12 = Tensor (defined in tensor.h)
577 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(13, std::string)
578 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(14, bool)
579 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(15, uint16_t)
580 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(16, char)
581 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(17, std::unique_ptr<std::mutex>)
582 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(18, std::unique_ptr<std::atomic<bool>>)
583 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(19, std::vector<int32_t>)
584 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(20, std::vector<int64_t>)
585 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(21, std::vector<unsigned long>)
586 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(22, bool*)
587 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(23, char*)
588 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(24, int*)
589 
590 // For some of the compilers, long is definied separately from int32_t and
591 // int64_t. As a result we will need to actually define them separately.
592 // It is recommended that one does NOT use long - use int32_t and int64_t
593 // explicitly. Explicit long type annotation may go away in the future.
594 // details: This hack works by defining a _guard_long_unique type, which is
595 // long iff the compiler has a separate long type and is a dummy type otherwise.
596 // we then allocate a type id to that _guard_long_unique. If the compiler has a
597 // separate long type, this allocates a type id for long. Otherwise, it
598 // allocates a type id for the dummy type, which doesn't matter.
599 namespace detail {
600 template <class T>
602 template <class T>
603 using _guard_long_unique = c10::guts::conditional_t<
604  std::is_same<long, int32_t>::value || std::is_same<long, int64_t>::value,
606  T>;
607 } // namespace detail
608 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(25, detail::_guard_long_unique<long>)
609 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(
610  26,
611  detail::_guard_long_unique<std::vector<long>>)
612 
613 CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(27, _CaffeHighestPreallocatedTypeId)
614 } // namespace caffe2
constexpr size_t itemsize() const noexcept
Returns the size of the item.
Definition: typeid.h:365
void _Copy(const void *src, void *dst, size_t n)
Typed copy function for classes.
Definition: typeid.h:218
constexpr PlacementNew * placementNew() const noexcept
Returns the placement new function pointer for individual items.
Definition: typeid.h:374
void _PlacementDelete(void *ptr, size_t n)
Destructor for non-fundamental types.
Definition: typeid.h:260
A type id is a unique id for a given C++ type.
Definition: typeid.h:60
static TypeMeta Make()
Returns a TypeMeta object that corresponds to the typename T.
Definition: typeid.h:427
constexpr PlacementDelete * placementDelete() const noexcept
Returns the destructor function pointer for individual items.
Definition: typeid.h:386
constexpr const char * name() const noexcept
Returns a printable name for the type.
Definition: typeid.h:395
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
Definition: blob.h:13
void _PlacementNew(void *ptr, size_t n)
Placement new function for the type.
Definition: typeid.h:156
constexpr TypeIdentifier id() const noexcept
Returns the type id.
Definition: typeid.h:359
constexpr Copy * copy() const noexcept
Returns the typed copy function pointer for individual iterms.
Definition: typeid.h:380
Flush-To-Zero and Denormals-Are-Zero mode.
std::string demangle(const char *name)
Utility to demangle a C++ symbol name.
Definition: Type.cpp:23
TypeMeta is a thin class that allows us to store the type of a container such as a blob...
Definition: typeid.h:324
This template simplifies generation of simple classes that wrap an id in a typesafe way...
Definition: IdWrapper.h:26
void _CopyNotAllowed(const void *, void *, size_t)
A placeholder function for types that do not allow assignment.
Definition: typeid.h:230