#pragma once #include #include #include #include #include namespace c10 { /// MaybeOwnedTraits describes how to borrow from T. Here is how we /// can implement borrowing from an arbitrary type T using a raw /// pointer to const: template struct MaybeOwnedTraitsGenericImpl { using owned_type = T; using borrow_type = const T*; static borrow_type createBorrow(const owned_type& from) { return &from; } static void assignBorrow(borrow_type& lhs, borrow_type rhs) { lhs = rhs; } static void destroyBorrow(borrow_type& /*toDestroy*/) {} static const owned_type& referenceFromBorrow(const borrow_type& borrow) { return *borrow; } static const owned_type* pointerFromBorrow(const borrow_type& borrow) { return borrow; } static bool debugBorrowIsValid(const borrow_type& borrow) { return borrow != nullptr; } }; /// It is possible to eliminate the extra layer of indirection for /// borrows for some types that we control. For examples, see /// intrusive_ptr.h and TensorBody.h. template struct MaybeOwnedTraits; // Explicitly enable MaybeOwned>, rather than allowing // MaybeOwned to be used for any type right away. template struct MaybeOwnedTraits> : public MaybeOwnedTraitsGenericImpl> {}; /// A smart pointer around either a borrowed or owned T. When /// constructed with borrowed(), the caller MUST ensure that the /// borrowed-from argument outlives this MaybeOwned. Compare to /// Rust's std::borrow::Cow /// (https://doc.rust-lang.org/std/borrow/enum.Cow.html), but note /// that it is probably not suitable for general use because C++ has /// no borrow checking. Included here to support /// Tensor::expect_contiguous. template class MaybeOwned final { using borrow_type = typename MaybeOwnedTraits::borrow_type; using owned_type = typename MaybeOwnedTraits::owned_type; bool isBorrowed_; union { borrow_type borrow_; owned_type own_; }; /// Don't use this; use borrowed() instead. explicit MaybeOwned(const owned_type& t) : isBorrowed_(true), borrow_(MaybeOwnedTraits::createBorrow(t)) {} /// Don't use this; use owned() instead. explicit MaybeOwned(T&& t) noexcept(std::is_nothrow_move_constructible_v) : isBorrowed_(false), own_(std::move(t)) {} /// Don't use this; use owned() instead. template explicit MaybeOwned(std::in_place_t, Args&&... args) : isBorrowed_(false), own_(std::forward(args)...) {} public: explicit MaybeOwned() : isBorrowed_(true), borrow_() {} // Copying a borrow yields another borrow of the original, as with a // T*. Copying an owned T yields another owned T for safety: no // chains of borrowing by default! (Note you could get that behavior // with MaybeOwned::borrowed(*rhs) if you wanted it.) MaybeOwned(const MaybeOwned& rhs) : isBorrowed_(rhs.isBorrowed_) { if (C10_LIKELY(rhs.isBorrowed_)) { MaybeOwnedTraits::assignBorrow(borrow_, rhs.borrow_); } else { new (&own_) T(rhs.own_); } } MaybeOwned& operator=(const MaybeOwned& rhs) { if (this == &rhs) { return *this; } if (C10_UNLIKELY(!isBorrowed_)) { if (rhs.isBorrowed_) { own_.~T(); MaybeOwnedTraits::assignBorrow(borrow_, rhs.borrow_); isBorrowed_ = true; } else { own_ = rhs.own_; } } else { if (C10_LIKELY(rhs.isBorrowed_)) { MaybeOwnedTraits::assignBorrow(borrow_, rhs.borrow_); } else { MaybeOwnedTraits::destroyBorrow(borrow_); new (&own_) T(rhs.own_); isBorrowed_ = false; } } TORCH_INTERNAL_ASSERT_DEBUG_ONLY(isBorrowed_ == rhs.isBorrowed_); return *this; } MaybeOwned(MaybeOwned&& rhs) noexcept( // NOLINTNEXTLINE(*-noexcept-move-*) std::is_nothrow_move_constructible_v && std::is_nothrow_move_assignable_v) : isBorrowed_(rhs.isBorrowed_) { if (C10_LIKELY(rhs.isBorrowed_)) { MaybeOwnedTraits::assignBorrow(borrow_, rhs.borrow_); } else { new (&own_) T(std::move(rhs.own_)); } } MaybeOwned& operator=(MaybeOwned&& rhs) noexcept( std::is_nothrow_move_assignable_v && std::is_nothrow_move_assignable_v && std::is_nothrow_move_constructible_v && // NOLINTNEXTLINE(*-noexcept-move-*) std::is_nothrow_destructible_v && std::is_nothrow_destructible_v) { if (this == &rhs) { return *this; } if (C10_UNLIKELY(!isBorrowed_)) { if (rhs.isBorrowed_) { own_.~T(); MaybeOwnedTraits::assignBorrow(borrow_, rhs.borrow_); isBorrowed_ = true; } else { own_ = std::move(rhs.own_); } } else { if (C10_LIKELY(rhs.isBorrowed_)) { MaybeOwnedTraits::assignBorrow(borrow_, rhs.borrow_); } else { MaybeOwnedTraits::destroyBorrow(borrow_); new (&own_) T(std::move(rhs.own_)); isBorrowed_ = false; } } return *this; } static MaybeOwned borrowed(const T& t) { return MaybeOwned(t); } static MaybeOwned owned(T&& t) noexcept( std::is_nothrow_move_constructible_v) { return MaybeOwned(std::move(t)); } template static MaybeOwned owned(std::in_place_t, Args&&... args) { return MaybeOwned(std::in_place, std::forward(args)...); } ~MaybeOwned() noexcept( // NOLINTNEXTLINE(*-noexcept-destructor) std::is_nothrow_destructible_v && std::is_nothrow_destructible_v) { if (C10_UNLIKELY(!isBorrowed_)) { own_.~T(); } else { MaybeOwnedTraits::destroyBorrow(borrow_); } } // This is an implementation detail! You should know what you're doing // if you are testing this. If you just want to guarantee ownership move // this into a T bool unsafeIsBorrowed() const { return isBorrowed_; } const T& operator*() const& { if (isBorrowed_) { TORCH_INTERNAL_ASSERT_DEBUG_ONLY( MaybeOwnedTraits::debugBorrowIsValid(borrow_)); } return C10_LIKELY(isBorrowed_) ? MaybeOwnedTraits::referenceFromBorrow(borrow_) : own_; } const T* operator->() const { if (isBorrowed_) { TORCH_INTERNAL_ASSERT_DEBUG_ONLY( MaybeOwnedTraits::debugBorrowIsValid(borrow_)); } return C10_LIKELY(isBorrowed_) ? MaybeOwnedTraits::pointerFromBorrow(borrow_) : &own_; } // If borrowed, copy the underlying T. If owned, move from // it. borrowed/owned state remains the same, and either we // reference the same borrow as before or we are an owned moved-from // T. T operator*() && { if (isBorrowed_) { TORCH_INTERNAL_ASSERT_DEBUG_ONLY( MaybeOwnedTraits::debugBorrowIsValid(borrow_)); return MaybeOwnedTraits::referenceFromBorrow(borrow_); } else { return std::move(own_); } } }; } // namespace c10