// // A variant map is a heterogenous, type safe key->value map. It allows // for multiple different value types to be stored dynamically in the same map. // // It provides the following interface in a nutshell: // // struct VariantMap { // template <typename TValue> // TValue* Get(Key<T> key); // null if the value was never set, otherwise the value. // // template <typename TValue> // void Set(Key<T> key, TValue value); // }; // // Since the key is strongly typed at compile-time, it is impossible to accidentally // read/write a value with a different type than the key at either compile-time or run-time. // // Do not use VariantMap/VariantMapKey directly. Instead subclass each of them and use // the subclass, for example: // // template <typename TValue> // struct FruitMapKey : VariantMapKey<TValue> { // FruitMapKey() {} // }; // // struct FruitMap : VariantMap<FruitMap, FruitMapKey> { // // This 'using' line is necessary to inherit the variadic constructor. // using VariantMap<FruitMap, FruitMapKey>::VariantMap; // // // Make the next '4' usages of Key slightly shorter to type. // template <typename TValue> // using Key = FruitMapKey<TValue>; // // static const Key<int> Apple; // static const Key<double> Orange; // static const Key<std::string> Banana; // }; // // const FruitMap::Key<int> FruitMap::Apple; // const FruitMap::Key<double> FruitMap::Orange; // const FruitMap::Key<std::string> Banana; // // See variant_map_test.cc for more examples. //
// Implementation details for VariantMap. namespace detail { // Allocate a unique counter value each time it's called. struct VariantMapKeyCounterAllocator { static size_t AllocateCounter() { static size_t counter = 0;
counter++;
return counter;
}
};
// Type-erased version of VariantMapKey<T> struct VariantMapKeyRaw { // TODO: this may need to call a virtual function to support string comparisons booloperator<(const VariantMapKeyRaw& other) const { return key_counter_ < other.key_counter_;
}
// The following functions need to be virtual since we don't know the compile-time type anymore:
// Clone the key, creating a copy of the contents. virtual VariantMapKeyRaw* Clone() const = 0;
// Delete a value whose runtime type is that of the non-erased key's TValue. virtualvoid ValueDelete(void* value) const = 0;
// Clone a value whose runtime type is that of the non-erased key's TValue. virtualvoid* ValueClone(void* value) const = 0;
// Compare one key to another (same as operator<). virtualbool Compare(const VariantMapKeyRaw* other) const { if (other == nullptr) { returnfalse;
} return key_counter_ < other->key_counter_;
}
protected: // Avoid the object slicing problem; use Clone() instead.
VariantMapKeyRaw(const VariantMapKeyRaw&) = default;
VariantMapKeyRaw(VariantMapKeyRaw&&) noexcept = default;
private:
size_t key_counter_; // Runtime type ID. Unique each time a new type is reified.
};
} // namespace detail
// The base type for keys used by the VariantMap. Users must subclass this type. template <typename TValue> struct VariantMapKey : detail::VariantMapKeyRaw { // Instantiate a default value for this key. If an explicit default value was provided // then that is used. Otherwise, the default value for the type TValue{} is returned.
TValue CreateDefaultValue() const { if (default_value_ == nullptr) { return TValue{};
} else { return TValue(*default_value_);
}
}
template <typename Base, template <typename TV> class TKey> friendstruct VariantMap;
// Store a prototype of the key's default value, for usage with VariantMap::GetOrDefault
std::shared_ptr<TValue> default_value_;
};
// Implementation details for a stringified VariantMapStringKey. namespace detail { struct VariantMapStringKeyRegistry { // TODO
};
} // namespace detail
// Alternative base type for all keys used by VariantMap, supports runtime strings as the name. template <typename TValue> struct VariantMapStringKey : VariantMapKey<TValue> { explicit VariantMapStringKey(constchar* name)
: // VariantMapKey(/*std::hash<std::string>()(name)*/),
name_(name) {
}
private: constchar* name_;
};
// A variant map allows type-safe heteregeneous key->value mappings. // All possible key types must be specified at compile-time. Values may be added/removed // at runtime. template <typename Base, template <typename TV> class TKey> struct VariantMap { // Allow users of this static interface to use the key type. template <typename TValue> using Key = TKey<TValue>;
// Look up the value from the key. The pointer becomes invalid if this key is overwritten/removed. // A null value is returned only when the key does not exist in this map. template <typename TValue> const TValue* Get(const TKey<TValue>& key) const { return GetValuePtr(key);
}
// Look up the value from the key. The pointer becomes invalid if this key is overwritten/removed. // A null value is returned only when the key does not exist in this map. template <typename TValue>
TValue* Get(const TKey<TValue>& key) { return GetValuePtr(key);
}
// Look up the value from the key and return the value wrapped in a std::optional. If it was not // set in the map, return an empty std::optional. template <typename TValue>
std::optional<TValue> GetOptional(const TKey<TValue>& key) const { auto* ptr = Get(key); return (ptr == nullptr) ? std::optional<TValue>{} : std::make_optional(*ptr);
}
// Lookup the value from the key. If it was not set in the map, return the default value. // The default value is either the key's default, or TValue{} if the key doesn't have a default. template <typename TValue>
TValue GetOrDefault(const TKey<TValue>& key) const { auto* ptr = Get(key); return (ptr == nullptr) ? key.CreateDefaultValue() : *ptr;
}
private: // TODO: move to detail, or make it more generic like a ScopeGuard(function) template <typename TValue> struct ScopedRemove {
ScopedRemove(VariantMap& map, const TKey<TValue>& key) : map_(map), key_(key) {}
~ScopedRemove() {
map_.Remove(key_);
}
VariantMap& map_; const TKey<TValue>& key_;
};
public: // Release the value from the key. If it was not set in the map, returns the default value. // If the key was set, it is removed as a side effect. template <typename TValue>
TValue ReleaseOrDefault(const TKey<TValue>& key) {
ScopedRemove<TValue> remove_on_return(*this, key);
// See if a value is stored for this key. template <typename TValue> bool Exists(const TKey<TValue>& key) const { return GetKeyValueIterator(key) != storage_map_.end();
}
// Set a value for a given key, overwriting the previous value if any. // Note: Omit the `value` from TValue type deduction, deduce only from the `key` argument. template <typename TValue> void Set(const TKey<TValue>& key, consttypename Identity<TValue>::type& value) { // Clone the value first, to protect against &value == GetValuePtr(key). auto* new_value = new TValue(value);
Remove(key); bool inserted = storage_map_.insert({key.Clone(), new_value}).second;
DCHECK(inserted); // ensure key.Clone() does not leak memory.
}
// Set a value for a given key, only if there was no previous value before. // Returns true if the value was set, false if a previous value existed. // Note: Omit the `value` from TValue type deduction, deduce only from the `key` argument. template <typename TValue> bool SetIfMissing(const TKey<TValue>& key, consttypename Identity<TValue>::type& value) {
TValue* ptr = Get(key); if (ptr == nullptr) {
Set(key, value); returntrue;
} returnfalse;
}
// Remove the value for a given key, or a no-op if there was no previously set value. template <typename TValue> void Remove(const TKey<TValue>& key) {
StaticAssertKeyType<TValue>();
auto&& it = GetKeyValueIterator(key); if (it != storage_map_.end()) {
key.ValueDelete(it->second); delete it->first;
storage_map_.erase(it);
}
}
// How many key/value pairs are stored in this map.
size_t Size() const { return storage_map_.size();
}
// Construct an empty map.
VariantMap() {}
template <typename ... TKeyValue> explicit VariantMap(const TKeyValue& ... key_value_list) {
static_assert(sizeof...(TKeyValue) % 2 == 0, "Must be an even number of key/value elements");
InitializeParameters(key_value_list...);
}
// Create a new map from an existing map, copying all the key/value pairs.
VariantMap(const VariantMap& other) { operator=(other);
}
// Copy the key/value pairs from the other map into this one. Existing key/values are cleared.
VariantMap& operator=(const VariantMap& other) { if (this == &other) { return *this;
}
Clear();
for (auto&& kv_pair : other.storage_map_) { const detail::VariantMapKeyRaw* raw_key_other = kv_pair.first; void* value = kv_pair.second;
// Create a new map by moving an existing map into this one. The other map becomes empty.
VariantMap(VariantMap&& other) noexcept { operator=(std::forward<VariantMap>(other));
}
// Move the existing map's key/value pairs into this one. The other map becomes empty.
VariantMap& operator=(VariantMap&& other) noexcept { if (this != &other) {
Clear();
storage_map_.swap(other.storage_map_);
other.storage_map_.clear();
} return *this;
}
~VariantMap() {
DeleteStoredValues();
}
private: void InitializeParameters() {}
template <typename TK, typename TValue, typename ... Rest> void InitializeParameters(const TK& key, const TValue& value, const Rest& ... rest) {
static_assert(std::is_same_v<TK, TKey<TValue>>, "The 0th/2nd/4th/etc parameters must be a key");
// Custom key comparator for std::map, needed since we are storing raw pointers as the keys. struct KeyComparator { booloperator()(const detail::VariantMapKeyRaw* lhs, const detail::VariantMapKeyRaw* rhs) const { if (lhs == nullptr) { return lhs != rhs;
}
return lhs->Compare(rhs);
}
};
// Map of key pointers to value pointers. Pointers are never null. using StorageMap = std::map<const detail::VariantMapKeyRaw*, void*, KeyComparator>;
template <typename TValue> staticvoid StaticAssertKeyType() {
static_assert(std::is_base_of_v<VariantMapKey<TValue>, TKey<TValue>>, "The provided key type (TKey) must be a subclass of VariantMapKey");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.