// Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: Apache-2.0 #ifndef OPENVDB_METADATA_METAMAP_HAS_BEEN_INCLUDED #define OPENVDB_METADATA_METAMAP_HAS_BEEN_INCLUDED #include "Metadata.h" #include "Types.h" #include "Exceptions.h" #include #include namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { /// Container that maps names (strings) to values of arbitrary types class OPENVDB_API MetaMap { public: using Ptr = SharedPtr; using ConstPtr = SharedPtr; using MetadataMap = std::map; using MetaIterator = MetadataMap::iterator; using ConstMetaIterator = MetadataMap::const_iterator; ///< @todo this should really iterate over a map of Metadata::ConstPtrs MetaMap() {} MetaMap(const MetaMap& other); virtual ~MetaMap() {} /// Return a copy of this map whose fields are shared with this map. MetaMap::Ptr copyMeta() const; /// Return a deep copy of this map that shares no data with this map. MetaMap::Ptr deepCopyMeta() const; /// Assign a deep copy of another map to this map. MetaMap& operator=(const MetaMap&); /// Unserialize metadata from the given stream. void readMeta(std::istream&); /// Serialize metadata to the given stream. void writeMeta(std::ostream&) const; /// @brief Insert a new metadata field or overwrite the value of an existing field. /// @details If a field with the given name doesn't already exist, add a new field. /// Otherwise, if the new value's type is the same as the existing field's value type, /// overwrite the existing value with new value. /// @throw TypeError if a field with the given name already exists, but its value type /// is not the same as the new value's /// @throw ValueError if the given field name is empty. void insertMeta(const Name&, const Metadata& value); /// @brief Deep copy all of the metadata fields from the given map into this map. /// @throw TypeError if any field in the given map has the same name as /// but a different value type than one of this map's fields. void insertMeta(const MetaMap&); /// Remove the given metadata field if it exists. void removeMeta(const Name&); //@{ /// @brief Return a pointer to the metadata with the given name. /// If no such field exists, return a null pointer. Metadata::Ptr operator[](const Name&); Metadata::ConstPtr operator[](const Name&) const; //@} //@{ /// @brief Return a pointer to a TypedMetadata object of type @c T and with the given name. /// If no such field exists or if there is a type mismatch, return a null pointer. template typename T::Ptr getMetadata(const Name&); template typename T::ConstPtr getMetadata(const Name&) const; //@} /// @brief Return a reference to the value of type @c T stored in the given metadata field. /// @throw LookupError if no field with the given name exists. /// @throw TypeError if the given field is not of type @c T. template T& metaValue(const Name&); template const T& metaValue(const Name&) const; // Functions for iterating over the metadata MetaIterator beginMeta() { return mMeta.begin(); } MetaIterator endMeta() { return mMeta.end(); } ConstMetaIterator beginMeta() const { return mMeta.begin(); } ConstMetaIterator endMeta() const { return mMeta.end(); } void clearMetadata() { mMeta.clear(); } size_t metaCount() const { return mMeta.size(); } /// Return a string describing this metadata map. Prefix each line with @a indent. std::string str(const std::string& indent = "") const; /// Return @c true if the given map is equivalent to this map. bool operator==(const MetaMap& other) const; /// Return @c true if the given map is different from this map. bool operator!=(const MetaMap& other) const { return !(*this == other); } private: /// @brief Return a pointer to TypedMetadata with the given template parameter. /// @throw LookupError if no field with the given name is found. /// @throw TypeError if the given field is not of type T. template typename TypedMetadata::Ptr getValidTypedMetadata(const Name&) const; MetadataMap mMeta; }; /// Write a MetaMap to an output stream std::ostream& operator<<(std::ostream&, const MetaMap&); //////////////////////////////////////// inline Metadata::Ptr MetaMap::operator[](const Name& name) { MetaIterator iter = mMeta.find(name); return (iter == mMeta.end() ? Metadata::Ptr() : iter->second); } inline Metadata::ConstPtr MetaMap::operator[](const Name &name) const { ConstMetaIterator iter = mMeta.find(name); return (iter == mMeta.end() ? Metadata::Ptr() : iter->second); } //////////////////////////////////////// template inline typename T::Ptr MetaMap::getMetadata(const Name &name) { ConstMetaIterator iter = mMeta.find(name); if (iter == mMeta.end()) return typename T::Ptr{}; // To ensure that we get valid conversion if the metadata pointers cross dso // boundaries, we have to check the qualified typename and then do a static // cast. This is slower than doing a dynamic_pointer_cast, but is safer when // pointers cross dso boundaries. if (iter->second->typeName() == T::staticTypeName()) { return StaticPtrCast(iter->second); } // else return typename T::Ptr{}; } template inline typename T::ConstPtr MetaMap::getMetadata(const Name &name) const { ConstMetaIterator iter = mMeta.find(name); if (iter == mMeta.end()) return typename T::ConstPtr{}; // To ensure that we get valid conversion if the metadata pointers cross dso // boundaries, we have to check the qualified typename and then do a static // cast. This is slower than doing a dynamic_pointer_cast, but is safer when // pointers cross dso boundaries. if (iter->second->typeName() == T::staticTypeName()) { return StaticPtrCast(iter->second); } // else return typename T::ConstPtr{}; } //////////////////////////////////////// template inline typename TypedMetadata::Ptr MetaMap::getValidTypedMetadata(const Name &name) const { ConstMetaIterator iter = mMeta.find(name); if (iter == mMeta.end()) OPENVDB_THROW(LookupError, "Cannot find metadata " << name); // To ensure that we get valid conversion if the metadata pointers cross dso // boundaries, we have to check the qualified typename and then do a static // cast. This is slower than doing a dynamic_pointer_cast, but is safer when // pointers cross dso boundaries. typename TypedMetadata::Ptr m; if (iter->second->typeName() == TypedMetadata::staticTypeName()) { m = StaticPtrCast, Metadata>(iter->second); } if (!m) OPENVDB_THROW(TypeError, "Invalid type for metadata " << name); return m; } //////////////////////////////////////// template inline T& MetaMap::metaValue(const Name &name) { typename TypedMetadata::Ptr m = getValidTypedMetadata(name); return m->value(); } template inline const T& MetaMap::metaValue(const Name &name) const { typename TypedMetadata::Ptr m = getValidTypedMetadata(name); return m->value(); } } // namespace OPENVDB_VERSION_NAME } // namespace openvdb #endif // OPENVDB_METADATA_METAMAP_HAS_BEEN_INCLUDED