|
| 1 | +#pragma once |
| 2 | + |
| 3 | +#include <cstddef> |
| 4 | +#include <sstream> |
| 5 | +#include <type_traits> |
| 6 | +#include <typeinfo> |
| 7 | +#include <vector> |
| 8 | + |
| 9 | +#include <ATen/core/typeid.h> |
| 10 | +#include <c10/macros/Macros.h> |
| 11 | + |
| 12 | +namespace caffe2 { |
| 13 | + |
| 14 | +class Tensor; |
| 15 | + |
| 16 | +/** |
| 17 | + * @brief Blob is a general container that hosts a typed pointer. |
| 18 | + * |
| 19 | + * A Blob hosts a pointer as well as its type, and takes charge of deleting it |
| 20 | + * properly when the blob is deallocated or re-allocated with a new type. A blob |
| 21 | + * could contain anything, although the most common case is to contain a Tensor. |
| 22 | + */ |
| 23 | +class CAFFE2_API Blob final { |
| 24 | + public: |
| 25 | + using DestroyCall = void(void*); |
| 26 | + |
| 27 | + /** |
| 28 | + * Initializes an empty Blob. |
| 29 | + */ |
| 30 | + Blob() noexcept : meta_(), pointer_(nullptr), destroy_(nullptr) {} |
| 31 | + ~Blob() { |
| 32 | + Reset(); |
| 33 | + } |
| 34 | + |
| 35 | + Blob(Blob&& other) noexcept : Blob() { |
| 36 | + swap(other); |
| 37 | + } |
| 38 | + |
| 39 | + Blob& operator=(Blob&& other) noexcept { |
| 40 | + Blob(std::move(other)).swap(*this); |
| 41 | + return *this; |
| 42 | + } |
| 43 | + |
| 44 | + /** |
| 45 | + * Checks if the content stored in the blob is of type T. |
| 46 | + */ |
| 47 | + template <class T> |
| 48 | + bool IsType() const noexcept { |
| 49 | + return meta_.Match<T>(); |
| 50 | + } |
| 51 | + |
| 52 | + /** |
| 53 | + * Returns the meta info of the blob. |
| 54 | + */ |
| 55 | + inline const TypeMeta& meta() const noexcept { |
| 56 | + return meta_; |
| 57 | + } |
| 58 | + |
| 59 | + /** |
| 60 | + * Returns a printable typename of the blob. |
| 61 | + */ |
| 62 | + inline const char* TypeName() const noexcept { |
| 63 | + return meta_.name(); |
| 64 | + } |
| 65 | + |
| 66 | + /** |
| 67 | + * @brief Gets the const reference of the stored object. The code checks if |
| 68 | + * the stored object is of the desired type. |
| 69 | + */ |
| 70 | + // TODO(jerryzh): add a Get(DeviceType) function? |
| 71 | + template <class T> |
| 72 | + const T& Get() const { |
| 73 | + AT_ASSERTM( |
| 74 | + IsType<T>(), |
| 75 | + "wrong type for the Blob instance. Blob contains ", |
| 76 | + meta_.name(), |
| 77 | + " while caller expects ", |
| 78 | + TypeMeta::TypeName<T>()); |
| 79 | + // TODO: after we add Get<Tensor>(DeviceType) |
| 80 | + // and changed all the callsites, we can add |
| 81 | + // a static assert here to enforce T != Tensor |
| 82 | + return *static_cast<const T*>(pointer_); |
| 83 | + } |
| 84 | + |
| 85 | + const void* GetRaw() const noexcept { |
| 86 | + return pointer_; |
| 87 | + } |
| 88 | + void* GetRaw() noexcept { |
| 89 | + return pointer_; |
| 90 | + } |
| 91 | + |
| 92 | + /** |
| 93 | + * @brief Gets a mutable pointer to the stored object. |
| 94 | + * |
| 95 | + * If the current object is not of the right type, a new object is created |
| 96 | + * and the old object is freed. Note that type T should have a default |
| 97 | + * constructor. Otherwise, create the object yourself first, and use |
| 98 | + * Reset(). |
| 99 | + */ |
| 100 | + template <class T> |
| 101 | + T* GetMutable() { |
| 102 | + static_assert( |
| 103 | + std::is_default_constructible<T>::value, |
| 104 | + "GetMutable can't be called with non-default-constructible types. " |
| 105 | + "Try using specialized methods"); |
| 106 | + if (IsType<T>()) { |
| 107 | + return static_cast<T*>(pointer_); |
| 108 | + } else { |
| 109 | + // TODO Re-enable logging |
| 110 | + // VLOG(1) << "Create new mutable object " << TypeMeta::TypeName<T>(); |
| 111 | + return Reset<T>(new T()); |
| 112 | + } |
| 113 | + } |
| 114 | + |
| 115 | + template <class T> |
| 116 | + T* GetMutableOrNull() { |
| 117 | + if (IsType<T>()) { |
| 118 | + return static_cast<T*>(pointer_); |
| 119 | + } else { |
| 120 | + return nullptr; |
| 121 | + } |
| 122 | + } |
| 123 | + |
| 124 | + /** |
| 125 | + * Sets the underlying object to the allocated one. The Blob then takes over |
| 126 | + * the ownership of the passed in pointer. If there is already an object in |
| 127 | + * the Blob, the old object is freed. |
| 128 | + * |
| 129 | + * This is used when the underlying class T does not have a default ctor, or |
| 130 | + * complex initializations needs to be done outside the blob. |
| 131 | + */ |
| 132 | + template <class T> |
| 133 | + T* Reset(T* allocated) { |
| 134 | + if (pointer_ && destroy_) { |
| 135 | + destroy_(pointer_); |
| 136 | + } |
| 137 | + meta_ = TypeMeta::Make<T>(); |
| 138 | + pointer_ = static_cast<void*>(allocated); |
| 139 | + destroy_ = &Destroy<T>; |
| 140 | + return allocated; |
| 141 | + } |
| 142 | + |
| 143 | + inline void* Reset( |
| 144 | + void* allocated, |
| 145 | + const TypeMeta& meta, |
| 146 | + DestroyCall* destroy) { |
| 147 | + if (pointer_ && destroy_) { |
| 148 | + destroy_(pointer_); |
| 149 | + } |
| 150 | + meta_ = meta; |
| 151 | + pointer_ = static_cast<void*>(allocated); |
| 152 | + destroy_ = destroy; |
| 153 | + return allocated; |
| 154 | + } |
| 155 | + |
| 156 | + /** |
| 157 | + * Releases the ownership, if any, this Blob has on the underlying pointer. |
| 158 | + * The user is then responsible for freeing the data if needed |
| 159 | + */ |
| 160 | + inline DestroyCall* Release() { |
| 161 | + DestroyCall* d = destroy_; |
| 162 | + destroy_ = nullptr; |
| 163 | + return d; |
| 164 | + } |
| 165 | + |
| 166 | + /** |
| 167 | + * Sets the underlying object to the allocated one, but does not take over |
| 168 | + * the ownership of the passed in pointer. If there is already an object in |
| 169 | + * the Blob, the old object is freed. |
| 170 | + * |
| 171 | + * Unlike Reset, this does not take over the ownership of the pointer and the |
| 172 | + * caller is responsible for making sure that the lifetime of the allocated |
| 173 | + * blob outlasts the lifetime of any access to this blob, until another Reset |
| 174 | + * call is made or the blob is destructed. |
| 175 | + */ |
| 176 | + template <class T> |
| 177 | + typename std::remove_const<T>::type* ShareExternal( |
| 178 | + typename std::remove_const<T>::type* allocated) { |
| 179 | + return static_cast<T*>(ShareExternal( |
| 180 | + static_cast<void*>(allocated), |
| 181 | + TypeMeta::Make<typename std::remove_const<T>::type>())); |
| 182 | + } |
| 183 | + |
| 184 | + void* ShareExternal(void* allocated, const TypeMeta& meta) { |
| 185 | + if (pointer_ && destroy_) { |
| 186 | + destroy_(pointer_); |
| 187 | + } |
| 188 | + meta_ = meta; |
| 189 | + pointer_ = static_cast<void*>(allocated); |
| 190 | + destroy_ = nullptr; |
| 191 | + return allocated; |
| 192 | + } |
| 193 | + |
| 194 | + /** |
| 195 | + * Resets the Blob to an empty one. |
| 196 | + */ |
| 197 | + inline void Reset() { |
| 198 | + if (pointer_ && destroy_) { |
| 199 | + destroy_(pointer_); |
| 200 | + } |
| 201 | + pointer_ = nullptr; |
| 202 | + meta_ = TypeMeta(); |
| 203 | + destroy_ = nullptr; |
| 204 | + } |
| 205 | + |
| 206 | + /** |
| 207 | + * @brief Swaps the underlying storage of two blobs. |
| 208 | + */ |
| 209 | + void swap(Blob& rhs) { |
| 210 | + using std::swap; |
| 211 | + swap(meta_, rhs.meta_); |
| 212 | + swap(pointer_, rhs.pointer_); |
| 213 | + swap(destroy_, rhs.destroy_); |
| 214 | + } |
| 215 | + |
| 216 | + private: |
| 217 | + /** |
| 218 | + * @brief A destroy call that is used to properly deconstruct objects. |
| 219 | + */ |
| 220 | + template <class T> |
| 221 | + static void Destroy(void* pointer) { |
| 222 | + delete static_cast<T*>(pointer); |
| 223 | + } |
| 224 | + TypeMeta meta_; |
| 225 | + void* pointer_ = nullptr; |
| 226 | + DestroyCall* destroy_ = nullptr; |
| 227 | + |
| 228 | + C10_DISABLE_COPY_AND_ASSIGN(Blob); |
| 229 | +}; |
| 230 | + |
| 231 | +inline void swap(Blob& lhs, Blob& rhs) { |
| 232 | + lhs.swap(rhs); |
| 233 | +} |
| 234 | + |
| 235 | +} // namespace caffe2 |
0 commit comments