From 6cc74c662b862599141a624af74a4d5b2881321d Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 10 Nov 2018 16:29:24 +0800 Subject: Fix the extension name of any_map.hpp --- src/any_map.hpp | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 src/any_map.hpp (limited to 'src/any_map.hpp') diff --git a/src/any_map.hpp b/src/any_map.hpp new file mode 100644 index 00000000..ea6044b1 --- /dev/null +++ b/src/any_map.hpp @@ -0,0 +1,100 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "base.hpp" +#include "format.hpp" + + +namespace cru +{ + // A map with String as key and any type as value. + // It also has notification when value with specified key changed. + class AnyMap : public Object + { + public: + using ListenerToken = long; + using Listener = std::function; + + AnyMap() = default; + AnyMap(const AnyMap& other) = delete; + AnyMap(AnyMap&& other) = delete; + AnyMap& operator=(const AnyMap& other) = delete; + AnyMap& operator=(AnyMap&& other) = delete; + ~AnyMap() override = default; + + + // return the value if the value exists and the type of value is T. + // return a null optional if value doesn't exists. + // throw std::runtime_error if type is mismatch. + template + std::optional GetOptionalValue(const String& key) const + { + try + { + const auto find_result = map_.find(key); + if (find_result != map_.cend()) + { + const auto& value = find_result->second.first; + if (value.has_value()) + return std::any_cast(value); + return std::nullopt; + } + return std::nullopt; + } + catch (const std::bad_any_cast&) + { + throw std::runtime_error(Format("Value of key \"{}\" in AnyMap is not of the type {}.", ToUtf8String(key), typeid(T).name())); + } + } + + // return the value if the value exists and the type of value is T. + // throw if value doesn't exists. (different from "GetOptionalValue"). + // throw std::runtime_error if type is mismatch. + template + T GetValue(const String& key) const + { + const auto optional_value = GetOptionalValue(key); + if (optional_value.has_value()) + return optional_value.value(); + else + throw std::runtime_error(Format("Key \"{}\" does not exists in AnyMap.", ToUtf8String(key))); + } + + // Set the value of key, and trigger all related listeners. + template + void SetValue(const String& key, T&& value) + { + auto& p = map_[key]; + p.first = std::make_any(std::forward(value)); + InvokeListeners(p.second, p.first); + } + + // Remove the value of the key. + void ClearValue(const String& key) + { + auto& p = map_[key]; + p.first = std::any{}; + InvokeListeners(p.second, std::any{}); + } + + // Add a listener which is called when value of key is changed. + // Return a token used to remove the listener. + ListenerToken RegisterValueChangeListener(const String& key, const Listener& listener); + + // Remove a listener by token. + void UnregisterValueChangeListener(ListenerToken token); + + private: + void InvokeListeners(std::list& listener_list, const std::any& value); + + private: + std::unordered_map>> map_{}; + std::unordered_map listeners_{}; + ListenerToken current_listener_token_ = 0; + }; +} -- cgit v1.2.3