// // 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. // #include "pxr/pxr.h" #include "pxr/base/vt/value.h" #include "pxr/base/vt/typeHeaders.h" #include "pxr/base/vt/types.h" #include "pxr/base/vt/dictionary.h" #include "pxr/base/gf/math.h" #include "pxr/base/tf/instantiateSingleton.h" #include "pxr/base/tf/iterator.h" #include "pxr/base/tf/mallocTag.h" #include "pxr/base/tf/singleton.h" #include "pxr/base/tf/staticData.h" #include "pxr/base/tf/token.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using std::map; using std::string; using std::type_info; using std::vector; PXR_NAMESPACE_OPEN_SCOPE static_assert(std::is_nothrow_move_constructible::value && std::is_nothrow_move_assignable::value, ""); TF_REGISTRY_FUNCTION(TfType) { TfType::Define(); } template static inline VtValue _BoostNumericCast(const From x) { try { return VtValue(boost::numeric_cast(x)); } catch (const boost::bad_numeric_cast &) { return VtValue(); } } // If the To type has no infinity, simply use boost numeric_cast. template static typename std::enable_if::has_infinity,VtValue>::type _NumericCast(VtValue const &val) { return _BoostNumericCast(val.UncheckedGet()); } // If the To type has infinity, we convert values larger than the largest // finite value that To can take to infinity. template static typename std::enable_if::has_infinity,VtValue>::type _NumericCast(VtValue const &val) { const From x = val.UncheckedGet(); // Use 'x == x' to check that x is not NaN. NaNs don't compare equal to // themselves. if (x == x) { if (x > std::numeric_limits::max()) { return VtValue( std::numeric_limits::infinity()); } if (x < -std::numeric_limits::max()) { return VtValue(-std::numeric_limits::infinity()); } } return _BoostNumericCast(x); } template static void _RegisterNumericCasts() { VtValue::RegisterCast(_NumericCast); VtValue::RegisterCast(_NumericCast); } class Vt_CastRegistry { public: static Vt_CastRegistry &GetInstance() { return TfSingleton::GetInstance(); } void Register(type_info const &from, type_info const &to, VtValue (*castFn)(VtValue const &)) { std::type_index src = from; std::type_index dst = to; bool isNewEntry = _conversions.insert( std::make_pair(_ConversionSourceToTarget(src, dst), castFn)).second; if (!isNewEntry) { // This happens at startup if there's a bug in the code. TF_CODING_ERROR("VtValue cast already registered from " "'%s' to '%s'. New cast will be ignored.", ArchGetDemangled(from).c_str(), ArchGetDemangled(to).c_str()); return; } } VtValue PerformCast(type_info const &to, VtValue const &val) { if (val.IsEmpty()) return val; std::type_index src = val.GetTypeid(); std::type_index dst = to; VtValue (*castFn)(VtValue const &) = NULL; _Conversions::iterator c = _conversions.find({src, dst}); if (c != _conversions.end()) { castFn = c->second; } return castFn ? castFn(val) : VtValue(); } bool CanCast(type_info const &from, type_info const &to) { std::type_index src = from; std::type_index dst = to; return _conversions.find({src, dst}) != _conversions.end(); } private: Vt_CastRegistry() { TfSingleton::SetInstanceConstructed(*this); _RegisterBuiltinCasts(); TfRegistryManager::GetInstance().SubscribeTo(); } virtual ~Vt_CastRegistry() {} friend class TfSingleton; // Disambiguate TfToken->string conversion static VtValue _TfTokenToString(VtValue const &val) { return VtValue(val.UncheckedGet().GetString()); } static VtValue _TfStringToToken(VtValue const &val) { return VtValue(TfToken(val.UncheckedGet())); } void _RegisterBuiltinCasts() { _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); _RegisterNumericCasts(); VtValue::RegisterCast(_TfTokenToString); VtValue::RegisterCast(_TfStringToToken); } using _ConversionSourceToTarget = std::pair; struct _ConversionSourceToTargetHash { std::size_t operator()(_ConversionSourceToTarget p) const { std::size_t h = p.first.hash_code(); boost::hash_combine(h, p.second.hash_code()); return h; } }; using _Conversions = tbb::concurrent_unordered_map< _ConversionSourceToTarget, VtValue (*)(VtValue const &), _ConversionSourceToTargetHash>; _Conversions _conversions; }; TF_INSTANTIATE_SINGLETON(Vt_CastRegistry); // Force instantiation for the registry instance. ARCH_CONSTRUCTOR(Vt_CastRegistryInit, 255) { Vt_CastRegistry::GetInstance(); } bool VtValue::IsArrayValued() const { if (IsEmpty()) { return false; } if (ARCH_UNLIKELY(_IsProxy())) { return _info->IsArrayValued(_storage); } return _info->isArray; } const Vt_ShapeData* VtValue::_GetShapeData() const { return _info.GetLiteral() ? _info.Get()->GetShapeData(_storage) : nullptr; } size_t VtValue::_GetNumElements() const { return _info.GetLiteral() ? _info.Get()->GetNumElements(_storage) : 0; } std::type_info const & VtValue::GetTypeid() const { return _info.GetLiteral() ? _info.Get()->GetProxiedTypeid(_storage) : typeid(void); } std::type_info const & VtValue::GetElementTypeid() const { return _info.GetLiteral() ? _info.Get()->GetElementTypeid(_storage) : typeid(void); } TfType VtValue::GetType() const { if (IsEmpty()) { return TfType::Find(); } TfType t = ARCH_UNLIKELY(_IsProxy()) ? _info->GetProxiedType(_storage) : TfType::FindByTypeid(_info->typeInfo); if (t.IsUnknown()) { TF_WARN("Returning unknown type for VtValue with unregistered " "C++ type %s", ArchGetDemangled(GetTypeid()).c_str()); } return t; } std::string VtValue::GetTypeName() const { // XXX Why do we do this differently? In the proxy case we don't want to // require a type_info, but why not just always go thru TfType? if (ARCH_UNLIKELY(_IsProxy())) return GetType().GetTypeName(); else return ArchGetDemangled(GetTypeid()); } bool VtValue::CanHash() const { if (IsEmpty()) { return true; } if (ARCH_UNLIKELY(_IsProxy())) { return _info->CanHash(_storage); } return _info->isHashable; } size_t VtValue::GetHash() const { if (IsEmpty()) { return 0; } size_t h = _info->Hash(_storage); return h; } /* static */ VtValue VtValue::CastToTypeOf(VtValue const &val, VtValue const &other) { VtValue ret = val; return ret.CastToTypeOf(other); } /* static */ VtValue VtValue::CastToTypeid(VtValue const &val, std::type_info const &type) { VtValue ret = val; return ret.CastToTypeid(type); } void VtValue::_RegisterCast(type_info const &from, type_info const &to, VtValue (*castFn)(VtValue const &)) { Vt_CastRegistry::GetInstance().Register(from, to, castFn); } VtValue VtValue::_PerformCast(type_info const &to, VtValue const &val) { if (TfSafeTypeCompare(val.GetTypeid(), to)) return val; return Vt_CastRegistry::GetInstance().PerformCast(to, val); } bool VtValue::_CanCast(type_info const &from, type_info const &to) { if (TfSafeTypeCompare(from, to)) return true; return Vt_CastRegistry::GetInstance().CanCast(from, to); } bool VtValue::_EqualityImpl(VtValue const &rhs) const { // We're guaranteed by the caller that neither *this nor rhs are empty and // that _info and rhs._info do not point to the same object. if (ARCH_UNLIKELY(_IsProxy() != rhs._IsProxy())) { // Either one or the other are proxies, but not both. Check the types // first. If they match then resolve the proxy and compare with the // nonProxy. This way, proxies are only ever asked to compare to the // same proxy type, never to their proxied type. if (GetType() != rhs.GetType()) return false; VtValue const *proxy = _IsProxy() ? this : &rhs; VtValue const *nonProxy = _IsProxy() ? &rhs : this; void const *proxiedObj = proxy->_info->GetProxiedObjPtr(proxy->_storage); return proxiedObj && nonProxy->_info->EqualPtr(nonProxy->_storage, proxiedObj); } if (ARCH_UNLIKELY(_IsProxy() && rhs._IsProxy())) { // We have two different proxy types. In this case we just unbox them // both into VtValues and compare. return GetType() == rhs.GetType() && _info->GetProxiedAsVtValue(_storage) == rhs._info->GetProxiedAsVtValue(rhs._storage); } // Otherwise there are not proxies involved -- compare typeids and if they // match dispatch to the held type. return TfSafeTypeCompare(GetTypeid(), rhs.GetTypeid()) && _info->Equal(_storage, rhs._storage); } std::ostream & operator<<(std::ostream &out, const VtValue &self) { return self.IsEmpty() ? out : self._info->StreamOut(self._storage, out); } #ifdef PXR_PYTHON_SUPPORT_ENABLED TfPyObjWrapper VtValue::_GetPythonObject() const { return _info.GetLiteral() ? _info.Get()->GetPyObj(_storage) : TfPyObjWrapper(); } #endif // PXR_PYTHON_SUPPORT_ENABLED static void const * _FindOrCreateDefaultValue(std::type_info const &type, Vt_DefaultValueHolder (*factory)()) { // This function returns a default value for \a type. It stores a global // map from type name to value. If we have an entry for the requested type // in the map already, return that. Otherwise use \a factory to create a // new entry to store in the map, asserting that it produced a value of the // correct type. TfAutoMallocTag2 tag("Vt", "VtValue _FindOrCreateDefaultValue"); typedef map DefaultValuesMap; static DefaultValuesMap defaultValues; static tbb::spin_mutex defaultValuesMutex; string key = ArchGetDemangled(type); { // If there's already an entry for this type we can return it directly. tbb::spin_mutex::scoped_lock lock(defaultValuesMutex); DefaultValuesMap::iterator i = defaultValues.find(key); if (i != defaultValues.end()) return i->second.GetPointer(); } // We need to make a new entry. Call the factory function while the mutex // is unlocked. We do this because the factory is unknown code which could // plausibly call back into here, causing deadlock. Assert that the factory // produced a value of the correct type. Vt_DefaultValueHolder newValue = factory(); TF_AXIOM(TfSafeTypeCompare(newValue.GetType(), type)); // We now lock the mutex and attempt to insert the new value. This may fail // if another thread beat us to it while we were creating the new value and // weren't holding the lock. If this happens, we leak the default value we // created that isn't used. tbb::spin_mutex::scoped_lock lock(defaultValuesMutex); DefaultValuesMap::iterator i = defaultValues.emplace(std::move(key), std::move(newValue)).first; return i->second.GetPointer(); } bool VtValue::_TypeIsImpl(std::type_info const &qt) const { if (ARCH_UNLIKELY(_IsProxy())) { return _info->ProxyHoldsType(_storage, qt); } return false; } void const * VtValue::_FailGet(Vt_DefaultValueHolder (*factory)(), std::type_info const &queryType) const { // Issue a coding error detailing relevant types. if (IsEmpty()) { TF_CODING_ERROR("Attempted to get value of type '%s' from " "empty VtValue.", ArchGetDemangled(queryType).c_str()); } else { TF_CODING_ERROR("Attempted to get value of type '%s' from " "VtValue holding '%s'", ArchGetDemangled(queryType).c_str(), ArchGetDemangled(GetTypeid()).c_str()); } // Get a default value for query type, and use that. return _FindOrCreateDefaultValue(queryType, factory); } std::ostream & VtStreamOut(vector const &val, std::ostream &stream) { bool first = true; stream << '['; TF_FOR_ALL(i, val) { if (first) first = false; else stream << ", "; stream << *i; } stream << ']'; return stream; } #define _VT_IMPLEMENT_ZERO_VALUE_FACTORY(r, unused, elem) \ template <> \ Vt_DefaultValueHolder Vt_DefaultValueFactory::Invoke() \ { \ return Vt_DefaultValueHolder::Create(VtZero()); \ } \ template struct Vt_DefaultValueFactory; BOOST_PP_SEQ_FOR_EACH(_VT_IMPLEMENT_ZERO_VALUE_FACTORY, unused, VT_VEC_VALUE_TYPES VT_MATRIX_VALUE_TYPES VT_QUATERNION_VALUE_TYPES) PXR_NAMESPACE_CLOSE_SCOPE