#pragma once #include #include #include #include #include #include #include #include namespace caffe2 { class Tensor; /** * @brief Blob is a general container that hosts a typed pointer. * * A Blob hosts a pointer as well as its type, and takes charge of deleting it * properly when the blob is deallocated or re-allocated with a new type. A blob * could contain anything, although the most common case is to contain a Tensor. */ class CAFFE2_API Blob final : public c10::intrusive_ptr_target { public: /** * Initializes an empty Blob. */ Blob() noexcept : meta_(), pointer_(nullptr), has_ownership_(false) {} ~Blob() { Reset(); } Blob(Blob&& other) noexcept : Blob() { swap(other); } Blob& operator=(Blob&& other) noexcept { Blob(std::move(other)).swap(*this); return *this; } /** * Checks if the content stored in the blob is of type T. */ template bool IsType() const noexcept { return meta_.Match(); } /** * Returns the meta info of the blob. */ const TypeMeta& meta() const noexcept { return meta_; } /** * Returns a printable typename of the blob. */ const char* TypeName() const noexcept { return meta_.name(); } /** * @brief Gets the const reference of the stored object. The code checks if * the stored object is of the desired type. */ // TODO(jerryzh): add a Get(DeviceType) function? template const T& Get() const { AT_ASSERTM( IsType(), "wrong type for the Blob instance. Blob contains ", meta_.name(), " while caller expects ", TypeMeta::TypeName()); // TODO: after we add Get(DeviceType) // and changed all the callsites, we can add // a static assert here to enforce T != Tensor return *static_cast(pointer_); } const void* GetRaw() const noexcept { return pointer_; } void* GetRaw() noexcept { return pointer_; } /** * @brief Gets a mutable pointer to the stored object. * * If the current object is not of the right type, a new object is created * and the old object is freed. Note that type T should have a default * constructor. Otherwise, create the object yourself first, and use * Reset(). */ template T* GetMutable() { static_assert( std::is_default_constructible::value, "GetMutable can't be called with non-default-constructible types. " "Try using specialized methods"); if (IsType()) { return static_cast(pointer_); } else { // TODO Re-enable logging // VLOG(1) << "Create new mutable object " << TypeMeta::TypeName(); return Reset(new T()); } } template T* GetMutableOrNull() { if (IsType()) { return static_cast(pointer_); } else { return nullptr; } } /** * Sets the underlying object to the allocated one. The Blob then takes over * the ownership of the passed in pointer. If there is already an object in * the Blob, the old object is freed. * * This is used when the underlying class T does not have a default ctor, or * complex initializations needs to be done outside the blob. */ template T* Reset(T* allocated) { free_(); meta_ = TypeMeta::Make(); pointer_ = static_cast(allocated); has_ownership_ = true; return allocated; } /** * Sets the underlying object to the allocated one, but does not take over * the ownership of the passed in pointer. If there is already an object in * the Blob, the old object is freed. * * Unlike Reset, this does not take over the ownership of the pointer and the * caller is responsible for making sure that the lifetime of the allocated * blob outlasts the lifetime of any access to this blob, until another Reset * call is made or the blob is destructed. */ template typename std::remove_const::type* ShareExternal( typename std::remove_const::type* allocated) { return static_cast(ShareExternal( static_cast(allocated), TypeMeta::Make::type>())); } // TODO Remove ShareExternal() and have Blob always own its content void* ShareExternal(void* allocated, const TypeMeta& meta) { free_(); meta_ = meta; pointer_ = static_cast(allocated); has_ownership_ = false; return allocated; } /** * Resets the Blob to an empty one. */ void Reset() { free_(); pointer_ = nullptr; meta_ = TypeMeta(); has_ownership_ = false; } /** * @brief Swaps the underlying storage of two blobs. */ void swap(Blob& rhs) { using std::swap; swap(meta_, rhs.meta_); swap(pointer_, rhs.pointer_); swap(has_ownership_, rhs.has_ownership_); } private: void free_() { if (has_ownership_) { AT_ASSERTM(pointer_ != nullptr, "Can't have ownership of nullptr"); (*meta_.deleteFn())(pointer_); } } TypeMeta meta_; void* pointer_ = nullptr; bool has_ownership_ = false; C10_DISABLE_COPY_AND_ASSIGN(Blob); }; inline void swap(Blob& lhs, Blob& rhs) { lhs.swap(rhs); } inline std::ostream& operator<<(std::ostream& out, const Blob& v) { return out << "Blob[" << v.TypeName() << "]"; } } // namespace caffe2