// // Copyright 2016 Pixar // // Licensed under the Apache License, Version 2.0 (the "Apache License") // with the following modification; you may not use this file except in // compliance with the Apache License and the following modification to it: // Section 6. Trademarks. is deleted and replaced with: // // 6. Trademarks. This License does not grant permission to use the trade // names, trademarks, service marks, or product names of the Licensor // and its affiliates, except as required to comply with Section 4(c) of // the License and to reproduce the content of the NOTICE file. // // You may obtain a copy of the Apache License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the Apache License with the above modification is // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the Apache License for the specific // language governing permissions and limitations under the Apache License. // #ifndef PXR_BASE_VT_VALUE_H #define PXR_BASE_VT_VALUE_H #include "pxr/pxr.h" #ifdef PXR_PYTHON_SUPPORT_ENABLED // XXX: Include pyLock.h after pyObjWrapper.h to work around // Python include ordering issues. #include "pxr/base/tf/pyObjWrapper.h" #endif // PXR_PYTHON_SUPPORT_ENABLED #include "pxr/base/tf/pyLock.h" #include "pxr/base/arch/demangle.h" #include "pxr/base/arch/hints.h" #include "pxr/base/tf/anyUniquePtr.h" #include "pxr/base/tf/pointerAndBits.h" #include "pxr/base/tf/safeTypeCompare.h" #include "pxr/base/tf/stringUtils.h" #include "pxr/base/tf/tf.h" #include "pxr/base/tf/type.h" #include "pxr/base/vt/api.h" #include "pxr/base/vt/hash.h" #include "pxr/base/vt/streamOut.h" #include "pxr/base/vt/traits.h" #include "pxr/base/vt/types.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include PXR_NAMESPACE_OPEN_SCOPE /// Make a default value. /// VtValue uses this to create values to be returned from failed calls to \a /// Get. Clients may specialize this for their own types. template struct Vt_DefaultValueFactory; // This is a helper class used by Vt_DefaultValueFactory to return a value with // its type erased and only known at runtime via a std::type_info. struct Vt_DefaultValueHolder { // Creates a value-initialized object and stores the type_info for the // static type. template static Vt_DefaultValueHolder Create() { return Vt_DefaultValueHolder(TfAnyUniquePtr::New(), typeid(T)); } // Creates a copy of the object and stores the type_info for the static // type. template static Vt_DefaultValueHolder Create(T const &val) { return Vt_DefaultValueHolder(TfAnyUniquePtr::New(val), typeid(T)); } // Return the runtime type of the held object. std::type_info const &GetType() const { return *_type; } // Return a pointer to the held object. This may be safely cast to the // static type corresponding to the type_info returned by GetType. void const *GetPointer() const { return _ptr.Get(); } private: Vt_DefaultValueHolder(TfAnyUniquePtr &&ptr, std::type_info const &type) : _ptr(std::move(ptr)), _type(&type) {} TfAnyUniquePtr _ptr; std::type_info const *_type; }; class VtValue; // Overload VtStreamOut for vector. Produces output like [value1, // value2, ... valueN]. VT_API std::ostream &VtStreamOut(std::vector const &val, std::ostream &); #define VT_VALUE_SET_STORED_TYPE(SRC, DST) \ template <> struct Vt_ValueStoredType { typedef DST Type; } template struct Vt_ValueStoredType { typedef T Type; }; VT_VALUE_SET_STORED_TYPE(char const *, std::string); VT_VALUE_SET_STORED_TYPE(char *, std::string); #ifdef PXR_PYTHON_SUPPORT_ENABLED VT_VALUE_SET_STORED_TYPE(boost::python::object, TfPyObjWrapper); #endif // PXR_PYTHON_SUPPORT_ENABLED #undef VT_VALUE_SET_STORED_TYPE // A metafunction that gives the type VtValue should store for a given type T. template struct Vt_ValueGetStored : Vt_ValueStoredType::type> {}; /// Provides a container which may hold any type, and provides introspection /// and iteration over array types. See \a VtIsArray for more info. /// /// \section VtValue_Casting Held-type Conversion with VtValue::Cast /// /// VtValue provides a suite of "Cast" methods that convert or create a /// VtValue holding a requested type (via template parameter, typeid, or /// type-matching to another VtValue) from the type of the currently-held /// value. Clients can add conversions between their own types using the /// RegisterCast(), RegisterSimpleCast(), and /// RegisterSimpleBidirectionalCast() methods. Conversions from plugins can /// be guaranteed to be registered before they are needed by registering them /// from within a /// \code /// TF_REGISTRY_FUNCTION(VtValue) { /// } /// \endcode /// block. /// /// \subsection VtValue_builtin_conversions Builtin Type Conversion /// /// Conversions between most of the basic "value types" that are intrinsically /// convertible are builtin, including all numeric types (including Gf's \c /// half), std::string/TfToken, GfVec* (for vecs of the same dimension), and /// VtArray for floating-point POD and GfVec of the preceding. /// /// \subsection VtValue_numeric_conversion Numeric Conversion Safety /// /// The conversions between all scalar numeric types are performed with range /// checks such as provided by boost::numeric_cast(), and will fail, returning /// an empty VtValue if the source value is out of range of the destination /// type. /// /// Conversions between GfVec and other compound-numeric types provide no more /// or less safety or checking than the conversion constructors of the types /// themselves. This includes VtArray, even VtArray for T in scalar types /// that are range-checked when held singly. class VtValue { static const unsigned int _LocalFlag = 1 << 0; static const unsigned int _TrivialCopyFlag = 1 << 1; static const unsigned int _ProxyFlag = 1 << 2; template struct _Counted { explicit _Counted(T const &obj) : _obj(obj) { _refCount = 0; } bool IsUnique() const { return _refCount == 1; } T const &Get() const { return _obj; } T &GetMutable() { return _obj; } private: T _obj; mutable std::atomic _refCount; friend inline void intrusive_ptr_add_ref(_Counted const *d) { d->_refCount.fetch_add(1, std::memory_order_relaxed); } friend inline void intrusive_ptr_release(_Counted const *d) { if (d->_refCount.fetch_sub(1, std::memory_order_release) == 1) { std::atomic_thread_fence(std::memory_order_acquire); delete d; } } }; // Hold objects up to 1 word large locally. This makes the total structure // 16 bytes when compiled 64 bit (1 word type-info pointer, 1 word storage // space). static const size_t _MaxLocalSize = sizeof(void*); typedef std::aligned_storage< /* size */_MaxLocalSize, /* alignment */_MaxLocalSize>::type _Storage; template struct _IsTriviallyCopyable : boost::mpl::and_< boost::has_trivial_constructor, boost::has_trivial_copy, boost::has_trivial_assign, boost::has_trivial_destructor > {}; // Metafunction that returns true if T should be stored locally, false if it // should be stored remotely. template struct _UsesLocalStore : boost::mpl::bool_< (sizeof(T) <= sizeof(_Storage)) && VtValueTypeHasCheapCopy::value && std::is_nothrow_move_constructible::value && std::is_nothrow_move_assignable::value> {}; // Type information base class. struct _TypeInfo { private: using _CopyInitFunc = void (*)(_Storage const &, _Storage &); using _DestroyFunc = void (*)(_Storage &); using _MoveFunc = void (*)(_Storage &, _Storage &); using _CanHashFunc = bool (*)(_Storage const &); using _HashFunc = size_t (*)(_Storage const &); using _EqualFunc = bool (*)(_Storage const &, _Storage const &); using _EqualPtrFunc = bool (*)(_Storage const &, void const *); using _MakeMutableFunc = void (*)(_Storage &); #ifdef PXR_PYTHON_SUPPORT_ENABLED using _GetPyObjFunc = TfPyObjWrapper (*)(_Storage const &); #endif // PXR_PYTHON_SUPPORT_ENABLED using _StreamOutFunc = std::ostream & (*)(_Storage const &, std::ostream &); using _GetTypeidFunc = std::type_info const & (*)(_Storage const &); using _IsArrayValuedFunc = bool (*)(_Storage const &); using _GetElementTypeidFunc = std::type_info const & (*)(_Storage const &); using _GetShapeDataFunc = const Vt_ShapeData* (*)(_Storage const &); using _GetNumElementsFunc = size_t (*)(_Storage const &); using _ProxyHoldsTypeFunc = bool (*)(_Storage const &, std::type_info const &); using _GetProxiedTypeFunc = TfType (*)(_Storage const &); using _GetProxiedTypeidFunc = std::type_info const & (*)(_Storage const &); using _GetProxiedObjPtrFunc = void const *(*)(_Storage const &); using _GetProxiedAsVtValueFunc = VtValue (*)(_Storage const &); protected: constexpr _TypeInfo(const std::type_info &ti, const std::type_info &elementTi, bool isArray, bool isHashable, bool isProxy, _CopyInitFunc copyInit, _DestroyFunc destroy, _MoveFunc move, _CanHashFunc canHash, _HashFunc hash, _EqualFunc equal, _EqualPtrFunc equalPtr, _MakeMutableFunc makeMutable, #ifdef PXR_PYTHON_SUPPORT_ENABLED _GetPyObjFunc getPyObj, #endif // PXR_PYTHON_SUPPORT_ENABLED _StreamOutFunc streamOut, _GetTypeidFunc getTypeid, _IsArrayValuedFunc isArrayValued, _GetElementTypeidFunc getElementTypeid, _GetShapeDataFunc getShapeData, _GetNumElementsFunc getNumElements, _ProxyHoldsTypeFunc proxyHoldsType, _GetProxiedTypeFunc getProxiedType, _GetProxiedTypeidFunc getProxiedTypeid, _GetProxiedObjPtrFunc getProxiedObjPtr, _GetProxiedAsVtValueFunc getProxiedAsVtValue) : typeInfo(ti) , elementTypeInfo(elementTi) , isProxy(isProxy) , isArray(isArray) , isHashable(isHashable) // Function table , _copyInit(copyInit) , _destroy(destroy) , _move(move) , _canHash(canHash) , _hash(hash) , _equal(equal) , _equalPtr(equalPtr) , _makeMutable(makeMutable) #ifdef PXR_PYTHON_SUPPORT_ENABLED , _getPyObj(getPyObj) #endif // PXR_PYTHON_SUPPORT_ENABLED , _streamOut(streamOut) , _getTypeid(getTypeid) , _isArrayValued(isArrayValued) , _getElementTypeid(getElementTypeid) , _getShapeData(getShapeData) , _getNumElements(getNumElements) , _proxyHoldsType(proxyHoldsType) , _getProxiedType(getProxiedType) , _getProxiedTypeid(getProxiedTypeid) , _getProxiedObjPtr(getProxiedObjPtr) , _getProxiedAsVtValue(getProxiedAsVtValue) {} public: void CopyInit(_Storage const &src, _Storage &dst) const { _copyInit(src, dst); } void Destroy(_Storage &storage) const { _destroy(storage); } void Move(_Storage &src, _Storage &dst) const noexcept { _move(src, dst); } bool CanHash(_Storage const &storage) const { return _canHash(storage); } size_t Hash(_Storage const &storage) const { return _hash(storage); } bool Equal(_Storage const &lhs, _Storage const &rhs) const { return _equal(lhs, rhs); } bool EqualPtr(_Storage const &lhs, void const *rhs) const { return _equalPtr(lhs, rhs); } void MakeMutable(_Storage &storage) const { _makeMutable(storage); } #ifdef PXR_PYTHON_SUPPORT_ENABLED TfPyObjWrapper GetPyObj(_Storage const &storage) const { return _getPyObj(storage); } #endif // PXR_PYTHON_SUPPORT_ENABLED std::ostream &StreamOut(_Storage const &storage, std::ostream &out) const { return _streamOut(storage, out); } bool IsArrayValued(_Storage const &storage) const { return _isArrayValued(storage); } std::type_info const &GetElementTypeid(_Storage const &storage) const { return _getElementTypeid(storage); } std::type_info const &GetTypeid(_Storage const &storage) const { return _getTypeid(storage); } const Vt_ShapeData* GetShapeData(_Storage const &storage) const { return _getShapeData(storage); } size_t GetNumElements(_Storage const &storage) const { return _getNumElements(storage); } bool ProxyHoldsType(_Storage const &storage, std::type_info const &t) const { return _proxyHoldsType(storage, t); } TfType GetProxiedType(_Storage const &storage) const { return _getProxiedType(storage); } std::type_info const &GetProxiedTypeid(_Storage const &storage) const { return _getProxiedTypeid(storage); } VtValue GetProxiedAsVtValue(_Storage const &storage) const { return _getProxiedAsVtValue(storage); } void const *GetProxiedObjPtr(_Storage const &storage) const { return _getProxiedObjPtr(storage); } const std::type_info &typeInfo; const std::type_info &elementTypeInfo; bool isProxy; bool isArray; bool isHashable; private: _CopyInitFunc _copyInit; _DestroyFunc _destroy; _MoveFunc _move; _CanHashFunc _canHash; _HashFunc _hash; _EqualFunc _equal; _EqualPtrFunc _equalPtr; _MakeMutableFunc _makeMutable; #ifdef PXR_PYTHON_SUPPORT_ENABLED _GetPyObjFunc _getPyObj; #endif // PXR_PYTHON_SUPPORT_ENABLED _StreamOutFunc _streamOut; _GetTypeidFunc _getTypeid; _IsArrayValuedFunc _isArrayValued; _GetElementTypeidFunc _getElementTypeid; _GetShapeDataFunc _getShapeData; _GetNumElementsFunc _getNumElements; _ProxyHoldsTypeFunc _proxyHoldsType; _GetProxiedTypeFunc _getProxiedType; _GetProxiedTypeidFunc _getProxiedTypeid; _GetProxiedObjPtrFunc _getProxiedObjPtr; _GetProxiedAsVtValueFunc _getProxiedAsVtValue; }; // Type-dispatching overloads. // Array type helper. template struct _ArrayHelper { static const Vt_ShapeData* GetShapeData(T const &) { return NULL; } static size_t GetNumElements(T const &) { return 0; } constexpr static std::type_info const &GetElementTypeid() { return typeid(void); } }; template struct _ArrayHelper< Array, typename std::enable_if::value>::type> { static const Vt_ShapeData* GetShapeData(Array const &obj) { return obj._GetShapeData(); } static size_t GetNumElements(Array const &obj) { return obj.size(); } constexpr static std::type_info const &GetElementTypeid() { return typeid(typename Array::ElementType); } }; // Function used in case T has equality comparison. template static inline auto _TypedProxyEqualityImpl(T const &a, T const &b, int) -> decltype(a == b) { return a == b; } // Function used in case T does not have equality comparison. template static inline bool _TypedProxyEqualityImpl(NoEqual const &a, NoEqual const &b, long) { return VtGetProxiedObject(a) == VtGetProxiedObject(b); } template static inline auto _ErasedProxyEqualityImpl(T const &a, T const &b, int) -> decltype(a == b) { return a == b; } // Function used in case T does not have equality comparison. template static inline bool _ErasedProxyEqualityImpl(NoEqual const &a, NoEqual const &b, long) { return *VtGetErasedProxiedVtValue(a) == *VtGetErasedProxiedVtValue(b); } // Proxy type helper. Base case handles non-proxies and typed proxies. template struct _ProxyHelper { using ProxiedType = typename VtGetProxiedType::type; static bool CanHash(T const &) { return VtIsHashable(); } static size_t Hash(T const &obj) { return VtHashValue(VtGetProxiedObject(obj)); } static bool Equal(T const &a, T const &b) { // We use the traditional int/long = 0 arg technique to disambiguate // overloads, so we can invoke equality comparison on the *proxy* // type if it provides one, or if it doesn't then invoke equality // comparison on the *proxied* type instead. return _TypedProxyEqualityImpl(a, b, 0); } #ifdef PXR_PYTHON_SUPPORT_ENABLED static TfPyObjWrapper GetPyObj(T const &obj) { ProxiedType const &p = VtGetProxiedObject(obj); TfPyLock lock; return boost::python::api::object(p); } #endif //PXR_PYTHON_SUPPORT_ENABLED static std::ostream &StreamOut(T const &obj, std::ostream &out) { return VtStreamOut(VtGetProxiedObject(obj), out); } static Vt_ShapeData const *GetShapeData(T const &obj) { return _ArrayHelper::GetShapeData( VtGetProxiedObject(obj)); } static size_t GetNumElements(T const &obj) { return _ArrayHelper::GetNumElements( VtGetProxiedObject(obj)); } static bool IsArrayValued(T const &) { return VtIsArray::value; } static std::type_info const &GetTypeid(T const &) { return typeid(ProxiedType); } static std::type_info const &GetElementTypeid(T const &) { return _ArrayHelper::GetElementTypeid(); } static VtValue GetProxiedAsVtValue(T const &obj) { return VtValue(VtGetProxiedObject(obj)); } static bool HoldsType(T const &tp, std::type_info const &query) { return TfSafeTypeCompare(typeid(ProxiedType), query); } static TfType GetTfType(T const &tp) { return TfType::Find(); } static void const *GetObjPtr(T const &tp) { return static_cast(&VtGetProxiedObject(tp)); } }; template struct _ProxyHelper< ErasedProxy, typename std::enable_if< VtIsErasedValueProxy::value>::type> { static bool CanHash(ErasedProxy const &proxy) { return VtGetErasedProxiedVtValue(proxy)->CanHash(); } static size_t Hash(ErasedProxy const &proxy) { return VtGetErasedProxiedVtValue(proxy)->GetHash(); } static bool Equal(ErasedProxy const &a, ErasedProxy const &b) { // We use the traditional int/long = 0 arg technique to disambiguate // overloads, so we can invoke equality comparison on the *proxy* // type if it provides one, or if it doesn't then invoke equality // comparison on the VtValue containing the *proxied* type instead. return _ErasedProxyEqualityImpl(a, b, 0); } #ifdef PXR_PYTHON_SUPPORT_ENABLED static TfPyObjWrapper GetPyObj(ErasedProxy const &obj) { VtValue const *val = VtGetErasedProxiedVtValue(obj); TfPyLock lock; return boost::python::api::object(*val); } #endif //PXR_PYTHON_SUPPORT_ENABLED static std::ostream & StreamOut(ErasedProxy const &obj, std::ostream &out) { return VtStreamOut(obj, out); } static Vt_ShapeData const *GetShapeData(ErasedProxy const &obj) { return VtGetErasedProxiedVtValue(obj)->_GetShapeData(); } static size_t GetNumElements(ErasedProxy const &obj) { return VtGetErasedProxiedVtValue(obj)->_GetNumElements(); } static bool IsArrayValued(ErasedProxy const &obj) { return VtGetErasedProxiedVtValue(obj)->IsArrayValued(); } static std::type_info const &GetTypeid(ErasedProxy const &obj) { return VtGetErasedProxiedVtValue(obj)->GetTypeid(); } static std::type_info const &GetElementTypeid(ErasedProxy const &obj) { return VtGetErasedProxiedVtValue(obj)->GetElementTypeid(); } static VtValue GetProxiedAsVtValue(ErasedProxy const &ep) { return *VtGetErasedProxiedVtValue(ep); } static bool HoldsType(ErasedProxy const &ep, std::type_info const &query) { return VtErasedProxyHoldsType(ep, query); } static TfType GetTfType(ErasedProxy const &ep) { return VtGetErasedProxiedTfType(ep); } static void const *GetObjPtr(ErasedProxy const &ep) { VtValue const *val = VtGetErasedProxiedVtValue(ep); return val ? val->_GetProxiedObjPtr() : nullptr; } }; // _TypeInfo implementation helper. This is a CRTP base that the // _LocalTypeInfo and _RemoteTypeInfo types derive. It wraps their // type-specific implementations with type-generic interfaces. template struct _TypeInfoImpl : public _TypeInfo { static const bool IsLocal = _UsesLocalStore::value; static const bool HasTrivialCopy = _IsTriviallyCopyable::value; static const bool IsProxy = VtIsValueProxy::value; using ProxyHelper = _ProxyHelper; using This = _TypeInfoImpl; constexpr _TypeInfoImpl() : _TypeInfo(typeid(T), _ArrayHelper::GetElementTypeid(), VtIsArray::value, VtIsHashable(), IsProxy, &This::_CopyInit, &This::_Destroy, &This::_Move, &This::_CanHash, &This::_Hash, &This::_Equal, &This::_EqualPtr, &This::_MakeMutable, #ifdef PXR_PYTHON_SUPPORT_ENABLED &This::_GetPyObj, #endif // PXR_PYTHON_SUPPORT_ENABLED &This::_StreamOut, &This::_GetTypeid, // Array support. &This::_IsArrayValued, &This::_GetElementTypeid, &This::_GetShapeData, &This::_GetNumElements, // Proxy support. &This::_ProxyHoldsType, &This::_GetProxiedType, &This::_GetProxiedTypeid, &This::_GetProxiedObjPtr, &This::_GetProxiedAsVtValue) {} //////////////////////////////////////////////////////////////////// // Typed API for client use. static T const &GetObj(_Storage const &storage) { return Derived::_GetObj(_Container(storage)); } static T &GetMutableObj(_Storage &storage) { return Derived::_GetMutableObj(_Container(storage)); } static void CopyInitObj(T const &objSrc, _Storage &dst) { Derived::_PlaceCopy(&_Container(dst), objSrc); } private: static_assert(sizeof(Container) <= sizeof(_Storage), "Container size cannot exceed storage size."); //////////////////////////////////////////////////////////////////// // _TypeInfo interface function implementations. static void _CopyInit(_Storage const &src, _Storage &dst) { new (&_Container(dst)) Container(_Container(src)); } static void _Destroy(_Storage &storage) { _Container(storage).~Container(); } static bool _CanHash(_Storage const &storage) { return ProxyHelper::CanHash(GetObj(storage)); } static size_t _Hash(_Storage const &storage) { return ProxyHelper::Hash(GetObj(storage)); } static bool _Equal(_Storage const &lhs, _Storage const &rhs) { // Equal is only ever invoked with an object of this specific type. // That is, we only ever ask a proxy to compare to a proxy; we never // ask a proxy to compare to the proxied object. return ProxyHelper::Equal(GetObj(lhs), GetObj(rhs)); } static bool _EqualPtr(_Storage const &lhs, void const *rhs) { // Equal is only ever invoked with an object of this specific type. // That is, we only ever ask a proxy to compare to a proxy; we never // ask a proxy to compare to the proxied object. return ProxyHelper::Equal( GetObj(lhs), *static_cast(rhs)); } static void _Move(_Storage &src, _Storage &dst) noexcept { new (&_Container(dst)) Container(std::move(_Container(src))); _Destroy(src); } static void _MakeMutable(_Storage &storage) { GetMutableObj(storage); } #ifdef PXR_PYTHON_SUPPORT_ENABLED static TfPyObjWrapper _GetPyObj(_Storage const &storage) { return ProxyHelper::GetPyObj(GetObj(storage)); } #endif // PXR_PYTHON_SUPPORT_ENABLED static std::ostream &_StreamOut( _Storage const &storage, std::ostream &out) { return ProxyHelper::StreamOut(GetObj(storage), out); } static std::type_info const &_GetTypeid(_Storage const &storage) { return ProxyHelper::GetTypeid(GetObj(storage)); } static bool _IsArrayValued(_Storage const &storage) { return ProxyHelper::IsArrayValued(GetObj(storage)); } static std::type_info const & _GetElementTypeid(_Storage const &storage) { return ProxyHelper::GetElementTypeid(GetObj(storage)); } static const Vt_ShapeData* _GetShapeData(_Storage const &storage) { return ProxyHelper::GetShapeData(GetObj(storage)); } static size_t _GetNumElements(_Storage const &storage) { return ProxyHelper::GetNumElements(GetObj(storage)); } static bool _ProxyHoldsType(_Storage const &storage, std::type_info const &t) { return ProxyHelper::HoldsType(GetObj(storage), t); } static TfType _GetProxiedType(_Storage const &storage) { return ProxyHelper::GetTfType(GetObj(storage)); } static std::type_info const & _GetProxiedTypeid(_Storage const &storage) { return ProxyHelper::GetTypeid(GetObj(storage)); } static void const * _GetProxiedObjPtr(_Storage const &storage) { return ProxyHelper::GetObjPtr(GetObj(storage)); } static VtValue _GetProxiedAsVtValue(_Storage const &storage) { return ProxyHelper::GetProxiedAsVtValue(GetObj(storage)); } //////////////////////////////////////////////////////////////////// // Internal helper -- cast type-generic storage to type-specific // container. static Container &_Container(_Storage &storage) { // XXX Will need std::launder in c++17. return *reinterpret_cast(&storage); } static Container const &_Container(_Storage const &storage) { // XXX Will need std::launder in c++17. return *reinterpret_cast(&storage); } }; //////////////////////////////////////////////////////////////////////// // Local-storage type info implementation. The container and the object are // the same -- there is no distinct container. template struct _LocalTypeInfo : _TypeInfoImpl< T, // type T, // container _LocalTypeInfo // CRTP > { constexpr _LocalTypeInfo() : _TypeInfoImpl>() {} // Get returns object directly. static T &_GetMutableObj(T &obj) { return obj; } static T const &_GetObj(T const &obj) { return obj; } // Place placement new's object directly. static void _PlaceCopy(T *dst, T const &src) { new (dst) T(src); } }; //////////////////////////////////////////////////////////////////////// // Remote-storage type info implementation. The container is an // intrusive_ptr to an object holder: _Counted. template struct _RemoteTypeInfo : _TypeInfoImpl< T, // type boost::intrusive_ptr<_Counted >, // container _RemoteTypeInfo // CRTP > { constexpr _RemoteTypeInfo() : _TypeInfoImpl< T, boost::intrusive_ptr<_Counted>, _RemoteTypeInfo>() {} typedef boost::intrusive_ptr<_Counted > Ptr; // Get returns object stored in the pointed-to _Counted. static T &_GetMutableObj(Ptr &ptr) { if (!ptr->IsUnique()) ptr.reset(new _Counted(ptr->Get())); return ptr->GetMutable(); } static T const &_GetObj(Ptr const &ptr) { return ptr->Get(); } // PlaceCopy() allocates a new _Counted with a copy of the object. static void _PlaceCopy(Ptr *dst, T const &src) { new (dst) Ptr(new _Counted(src)); } }; // Metafunction that returns the specific _TypeInfo subclass for T. template struct _TypeInfoFor { // return _UsesLocalStore(T) ? _LocalTypeInfo : _RemoteTypeInfo; typedef typename boost::mpl::if_<_UsesLocalStore, _LocalTypeInfo, _RemoteTypeInfo >::type Type; }; // Make sure char[N] is treated as a string. template struct _TypeInfoFor { // return _UsesLocalStore(T) ? _LocalTypeInfo : _RemoteTypeInfo; typedef typename boost::mpl::if_<_UsesLocalStore, _LocalTypeInfo, _RemoteTypeInfo >::type Type; }; // Runtime function to return a _TypeInfo base pointer to a specific // _TypeInfo subclass for type T. template static TfPointerAndBits GetTypeInfo() { typedef typename _TypeInfoFor::Type TI; static const TI ti; static constexpr unsigned int flags = (TI::IsLocal ? _LocalFlag : 0) | (TI::HasTrivialCopy ? _TrivialCopyFlag : 0) | (TI::IsProxy ? _ProxyFlag : 0); return TfPointerAndBits(&ti, flags); } // A helper that moves a held value to temporary storage, but keeps it alive // until the _HoldAside object is destroyed. This is used when assigning // over a VtValue that might own the object being assigned. For instance, // if I have a VtValue holding a map, and I reassign this // VtValue with one of the elements from the map, we must ensure that the // map isn't destroyed until after the assignment has taken place. friend struct _HoldAside; struct _HoldAside { explicit _HoldAside(VtValue *val) : info((val->IsEmpty() || val->_IsLocalAndTriviallyCopyable()) ? static_cast<_TypeInfo const *>(NULL) : val->_info.Get()) { if (info) info->Move(val->_storage, storage); } ~_HoldAside() { if (info) info->Destroy(storage); } _Storage storage; _TypeInfo const *info; }; template typename boost::enable_if< boost::is_same::Type> >::type _Init(T const &obj) { _info = GetTypeInfo(); typedef typename _TypeInfoFor::Type TypeInfo; TypeInfo::CopyInitObj(obj, _storage); } template typename boost::disable_if< boost::is_same::Type> >::type _Init(T const &obj) { _Init(typename Vt_ValueGetStored::Type(obj)); } public: /// Default ctor gives empty VtValue. VtValue() {} /// Copy construct with \p other. VtValue(VtValue const &other) { _Copy(other, *this); } /// Move construct with \p other. VtValue(VtValue &&other) noexcept { _Move(other, *this); } /// Construct a VtValue holding a copy of \p obj. /// /// If T is a char pointer or array, produce a VtValue holding a /// std::string. If T is boost::python::object, produce a VtValue holding /// a TfPyObjWrapper. template explicit VtValue(T const &obj) { _Init(obj); } /// Create a new VtValue, taking its contents from \p obj. /// /// This is equivalent to creating a VtValue holding a value-initialized /// \p T instance, then invoking swap(, obj), leaving obj in a /// default-constructed (value-initialized) state. In the case that \p /// obj is expensive to copy, it may be significantly faster to use this /// idiom when \p obj need not retain its contents: /// /// \code /// MyExpensiveObject obj = CreateObject(); /// return VtValue::Take(obj); /// \endcode /// /// Rather than: /// /// \code /// MyExpensiveObject obj = CreateObject(); /// return VtValue(obj); /// \endcode template static VtValue Take(T &obj) { VtValue ret; ret.Swap(obj); return ret; } /// Destructor. ~VtValue() { _Clear(); } /// Copy assignment from another \a VtValue. VtValue &operator=(VtValue const &other) { if (ARCH_LIKELY(this != &other)) _Copy(other, *this); return *this; } /// Move assignment from another \a VtValue. VtValue &operator=(VtValue &&other) noexcept { if (ARCH_LIKELY(this != &other)) _Move(other, *this); return *this; } #ifndef doxygen template inline typename boost::enable_if_c< _TypeInfoFor::Type::IsLocal && _TypeInfoFor::Type::HasTrivialCopy, VtValue &>::type operator=(T obj) { _Clear(); _Init(obj); return *this; } #endif /// Assignment operator from any type. #ifdef doxygen template VtValue& operator=(T const &obj); #else template typename boost::disable_if_c< _TypeInfoFor::Type::IsLocal && _TypeInfoFor::Type::HasTrivialCopy, VtValue &>::type operator=(T const &obj) { _HoldAside tmp(this); _Init(obj); return *this; } #endif /// Assigning a char const * gives a VtValue holding a std::string. VtValue &operator=(char const *cstr) { std::string tmp(cstr); _Clear(); _Init(tmp); return *this; } /// Assigning a char * gives a VtValue holding a std::string. VtValue &operator=(char *cstr) { return *this = const_cast(cstr); } /// Swap this with \a rhs. VtValue &Swap(VtValue &rhs) noexcept { // Do nothing if both empty. Otherwise general swap. if (!IsEmpty() || !rhs.IsEmpty()) { VtValue tmp; _Move(*this, tmp); _Move(rhs, *this); _Move(tmp, rhs); } return *this; } /// Overloaded swap() for generic code/stl/etc. friend void swap(VtValue &lhs, VtValue &rhs) { lhs.Swap(rhs); } /// Swap the held value with \a rhs. If this value is holding a T, // make an unqualified call to swap(, rhs). If this value is // not holding a T, replace the held value with a value-initialized T // instance first, then swap. #ifdef doxygen template void Swap(T &rhs); #else template typename boost::enable_if< boost::is_same::Type> >::type Swap(T &rhs) { if (!IsHolding()) *this = T(); UncheckedSwap(rhs); } #endif /// Swap the held value with \a rhs. This VtValue must be holding an /// object of type \p T. If it does not, this invokes undefined behavior. /// Use Swap() if this VtValue is not known to contain an object of type /// \p T. #ifdef doxygen template void UncheckedSwap(T &rhs); #else template typename boost::enable_if< boost::is_same::Type> >::type UncheckedSwap(T &rhs) { using std::swap; swap(_GetMutable(), rhs); } #endif /// \overload void UncheckedSwap(VtValue &rhs) { Swap(rhs); } /// Make this value empty and return the held \p T instance. If /// this value does not hold a \p T instance, make this value empty and /// return a default-constructed \p T. template T Remove() { T result; Swap(result); _Clear(); return result; } /// Make this value empty and return the held \p T instance. If this /// value does not hold a \p T instance, this method invokes undefined /// behavior. template T UncheckedRemove() { T result; UncheckedSwap(result); _Clear(); return result; } /// Return true if this value is holding an object of type \p T, false /// otherwise. template bool IsHolding() const { return _info.GetLiteral() && _TypeIs(); } /// Returns true iff this is holding an array type (see VtIsArray<>). VT_API bool IsArrayValued() const; /// Return the number of elements in the held value if IsArrayValued(), /// return 0 otherwise. size_t GetArraySize() const { return _GetNumElements(); } /// Returns the typeid of the type held by this value. VT_API std::type_info const &GetTypeid() const; /// Return the typeid of elements in a array valued type. If not /// holding an array valued type, return typeid(void). VT_API std::type_info const &GetElementTypeid() const; /// Returns the TfType of the type held by this value. VT_API TfType GetType() const; /// Return the type name of the held typeid. VT_API std::string GetTypeName() const; /// Returns a const reference to the held object if the held object /// is of type \a T. Invokes undefined behavior otherwise. This is the /// fastest \a Get() method to use after a successful \a IsHolding() check. template T const &UncheckedGet() const { return _Get(); } /// Returns a const reference to the held object if the held object /// is of type \a T. Issues an error and returns a const reference to a /// default value if the held object is not of type \a T. Use \a IsHolding /// to verify correct type before calling this function. The default value /// returned in case of type mismatch is constructed using /// Vt_DefaultValueFactory. That may be specialized for client types. /// The default implementation of the default value factory produces a /// value-initialized T. template T const &Get() const { typedef Vt_DefaultValueFactory Factory; // In the unlikely case that the types don't match, we obtain a default // value to return and issue an error via _FailGet. if (ARCH_UNLIKELY(!IsHolding())) { return *(static_cast( _FailGet(Factory::Invoke, typeid(T)))); } return _Get(); } /// Return a copy of the held object if the held object is of type T. /// Return a copy of the default value \a def otherwise. Note that this /// always returns a copy, as opposed to \a Get() which always returns a /// reference. template T GetWithDefault(T const &def = T()) const { return IsHolding() ? UncheckedGet() : def; } /// Register a cast from VtValue holding From to VtValue holding To. template static void RegisterCast(VtValue (*castFn)(VtValue const &)) { _RegisterCast(typeid(From), typeid(To), castFn); } /// Register a simple cast from VtValue holding From to VtValue // holding To. template static void RegisterSimpleCast() { _RegisterCast(typeid(From), typeid(To), _SimpleCast); } /// Register a two-way cast from VtValue holding From to VtValue /// holding To. template static void RegisterSimpleBidirectionalCast() { RegisterSimpleCast(); RegisterSimpleCast(); } /// Return a VtValue holding \c val cast to hold T. Return empty VtValue /// if cast fails. /// /// This Cast() function is safe to call in multiple threads as it does /// not mutate the operant \p val. /// /// \sa \ref VtValue_Casting template static VtValue Cast(VtValue const &val) { VtValue ret = val; return ret.Cast(); } /// Return a VtValue holding \c val cast to same type that \c other is /// holding. Return empty VtValue if cast fails. /// /// This Cast() function is safe to call in multiple threads as it does not /// mutate the operant \p val. /// /// \sa \ref VtValue_Casting VT_API static VtValue CastToTypeOf(VtValue const &val, VtValue const &other); /// Return a VtValue holding \a val cast to \a type. Return empty VtValue /// if cast fails. /// /// This Cast() function is safe to call in multiple threads as it does not /// mutate the operant \p val. /// /// \sa \ref VtValue_Casting VT_API static VtValue CastToTypeid(VtValue const &val, std::type_info const &type); /// Return if a value of type \a from can be cast to type \a to. /// /// \sa \ref VtValue_Casting static bool CanCastFromTypeidToTypeid(std::type_info const &from, std::type_info const &to) { return _CanCast(from, to); } /// Return \c this holding value type cast to T. This value is left /// empty if the cast fails. /// /// \note Since this method mutates this value, it is not safe to invoke on /// the same VtValue in multiple threads simultaneously. /// /// \sa \ref VtValue_Casting template VtValue &Cast() { if (IsHolding()) return *this; return *this = _PerformCast(typeid(T), *this); } /// Return \c this holding value type cast to same type that /// \c other is holding. This value is left empty if the cast fails. /// /// \note Since this method mutates this value, it is not safe to invoke on /// the same VtValue in multiple threads simultaneously. /// /// \sa \ref VtValue_Casting VtValue &CastToTypeOf(VtValue const &other) { return *this = _PerformCast(other.GetTypeid(), *this); } /// Return \c this holding value type cast to \a type. This value is /// left empty if the cast fails. /// /// \note Since this method mutates this value, it is not safe to invoke on /// the same VtValue in multiple threads simultaneously. /// /// \sa \ref VtValue_Casting VtValue &CastToTypeid(std::type_info const &type) { return *this = _PerformCast(type, *this); } /// Return if \c this can be cast to \a T. /// /// \sa \ref VtValue_Casting template bool CanCast() const { return _CanCast(GetTypeid(), typeid(T)); } /// Return if \c this can be cast to \a type. /// /// \sa \ref VtValue_Casting bool CanCastToTypeOf(VtValue const &other) const { return _CanCast(GetTypeid(), other.GetTypeid()); } /// Return if \c this can be cast to \a type. /// /// \sa \ref VtValue_Casting bool CanCastToTypeid(std::type_info const &type) const { return _CanCast(GetTypeid(), type); } /// Returns true iff this value is empty. bool IsEmpty() const { return _info.GetLiteral() == 0; } /// Return true if the held object provides a hash implementation. VT_API bool CanHash() const; /// Return a hash code for the held object by calling VtHashValue() on it. VT_API size_t GetHash() const; friend inline size_t hash_value(VtValue const &val) { return val.GetHash(); } /// Tests for equality. template friend bool operator == (VtValue const &lhs, T const &rhs) { typedef typename Vt_ValueGetStored::Type Stored; return lhs.IsHolding() && lhs.UncheckedGet() == rhs; } template friend bool operator == (T const &lhs, VtValue const &rhs) { return rhs == lhs; } /// Tests for inequality. template friend bool operator != (VtValue const &lhs, T const &rhs) { return !(lhs == rhs); } template friend bool operator != (T const &lhs, VtValue const &rhs) { return !(lhs == rhs); } /// Test two values for equality. bool operator == (const VtValue &rhs) const { bool empty = IsEmpty(), rhsEmpty = rhs.IsEmpty(); if (empty || rhsEmpty) { // Either one or both empty -- only equal if both empty. return empty == rhsEmpty; } if (_info.GetLiteral() == rhs._info.GetLiteral()) { // Holding identical types -- compare directly. return _info.Get()->Equal(_storage, rhs._storage); } return _EqualityImpl(rhs); } bool operator != (const VtValue &rhs) const { return !(*this == rhs); } /// Calls through to operator << on the held object. VT_API friend std::ostream & operator << (std::ostream &out, const VtValue &self); private: VT_API const Vt_ShapeData* _GetShapeData() const; VT_API size_t _GetNumElements() const; friend struct Vt_ValueShapeDataAccess; static inline void _Copy(VtValue const &src, VtValue &dst) { if (src.IsEmpty()) { dst._Clear(); return; } _HoldAside tmp(&dst); dst._info = src._info; if (src._IsLocalAndTriviallyCopyable()) { dst._storage = src._storage; } else { dst._info->CopyInit(src._storage, dst._storage); } } static inline void _Move(VtValue &src, VtValue &dst) noexcept { if (src.IsEmpty()) { dst._Clear(); return; } _HoldAside tmp(&dst); dst._info = src._info; if (src._IsLocalAndTriviallyCopyable()) { dst._storage = src._storage; } else { dst._info->Move(src._storage, dst._storage); } src._info.Set(nullptr, 0); } template inline bool _TypeIs() const { std::type_info const &t = typeid(T); bool cmp = TfSafeTypeCompare(_info->typeInfo, t); return ARCH_UNLIKELY(_IsProxy() && !cmp) ? _TypeIsImpl(t) : cmp; } VT_API bool _TypeIsImpl(std::type_info const &queriedType) const; VT_API bool _EqualityImpl(VtValue const &rhs) const; template typename boost::enable_if, Proxy &>::type _GetMutable() { typedef typename _TypeInfoFor::Type TypeInfo; return TypeInfo::GetMutableObj(_storage); } template typename boost::disable_if, T &>::type _GetMutable() { // If we are a proxy, collapse it out to the real value first. if (ARCH_UNLIKELY(_IsProxy())) { *this = _info->GetProxiedAsVtValue(_storage); } typedef typename _TypeInfoFor::Type TypeInfo; return TypeInfo::GetMutableObj(_storage); } template typename boost::enable_if, Proxy const &>::type _Get() const { typedef typename _TypeInfoFor::Type TypeInfo; return TypeInfo::GetObj(_storage); } template typename boost::disable_if, T const &>::type _Get() const { typedef typename _TypeInfoFor::Type TypeInfo; if (ARCH_UNLIKELY(_IsProxy())) { return *static_cast(_GetProxiedObjPtr()); } return TypeInfo::GetObj(_storage); } void const *_GetProxiedObjPtr() const { return _info->GetProxiedObjPtr(_storage); } // Helper invoked in case Get fails. Reports an error and returns a default // value for \a queryType. VT_API void const * _FailGet(Vt_DefaultValueHolder (*factory)(), std::type_info const &queryType) const; inline void _Clear() { // optimize for local types not to deref _info. if (_info.GetLiteral() && !_IsLocalAndTriviallyCopyable()) _info.Get()->Destroy(_storage); _info.Set(nullptr, 0); } inline bool _IsLocalAndTriviallyCopyable() const { unsigned int bits = _info.BitsAs(); return (bits & (_LocalFlag | _TrivialCopyFlag)) == (_LocalFlag | _TrivialCopyFlag); } inline bool _IsProxy() const { return _info.BitsAs() & _ProxyFlag; } VT_API static void _RegisterCast(std::type_info const &from, std::type_info const &to, VtValue (*castFn)(VtValue const &)); VT_API static VtValue _PerformCast(std::type_info const &to, VtValue const &val); VT_API static bool _CanCast(std::type_info const &from, std::type_info const &to); // helper template function for simple casts from From to To. template static VtValue _SimpleCast(VtValue const &val) { return VtValue(To(val.UncheckedGet())); } #ifdef PXR_PYTHON_SUPPORT_ENABLED // This grants friend access to a function in the wrapper file for this // class. This lets the wrapper reach down into a value to get a // boost::python wrapped object corresponding to the held type. This // facility is necessary to get the python API we want. friend TfPyObjWrapper Vt_GetPythonObjectFromHeldValue(VtValue const &self); VT_API TfPyObjWrapper _GetPythonObject() const; #endif // PXR_PYTHON_SUPPORT_ENABLED _Storage _storage; TfPointerAndBits _info; }; #ifndef doxygen /// Make a default value. VtValue uses this to create values to be returned /// from failed calls to \a Get. Clients may specialize this for their own /// types. template struct Vt_DefaultValueFactory { /// This function *must* return an object of type \a T. static Vt_DefaultValueHolder Invoke() { return Vt_DefaultValueHolder::Create(); } }; struct Vt_ValueShapeDataAccess { static const Vt_ShapeData* _GetShapeData(const VtValue& value) { return value._GetShapeData(); } static size_t _GetNumElements(const VtValue& value) { return value._GetNumElements(); } }; // For performance reasons, the default constructors for vectors, // matrices, and quaternions do *not* initialize the data of the // object. This greatly improves the performance of creating large // arrays of objects. However, boost::value_initialized() no // longer fills the memory of the object with 0 bytes before invoking // the constructor so we started getting errors complaining about // uninitialized values. So, we now use VtZero to construct zeroed // out vectors, matrices, and quaternions by explicitly instantiating // the factory for these types. // #define _VT_DECLARE_ZERO_VALUE_FACTORY(r, unused, elem) \ template <> \ VT_API Vt_DefaultValueHolder Vt_DefaultValueFactory::Invoke(); BOOST_PP_SEQ_FOR_EACH(_VT_DECLARE_ZERO_VALUE_FACTORY, unused, VT_VEC_VALUE_TYPES VT_MATRIX_VALUE_TYPES VT_QUATERNION_VALUE_TYPES) #undef _VT_DECLARE_ZERO_VALUE_FACTORY // // The Get()/IsHolding routines needs to be special-cased to handle getting a // VtValue *as* a VtValue. // template <> inline const VtValue& VtValue::Get() const { return *this; } template <> inline const VtValue& VtValue::UncheckedGet() const { return *this; } template <> inline bool VtValue::IsHolding() const { return true; } // Specialize VtValue::IsHolding() to always return false. template <> inline bool VtValue::IsHolding() const { return false; } #endif // !doxygen PXR_NAMESPACE_CLOSE_SCOPE #endif // PXR_BASE_VT_VALUE_H