diff options
author | crupest <crupest@outlook.com> | 2019-03-19 16:21:54 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2019-03-19 16:21:54 +0800 |
commit | 5dc738a57930271194bd86673eb86f149096a7b2 (patch) | |
tree | 71174aba0d1c0918cc7d7a1be0b86ec0d5c20401 /src/util | |
parent | 06edefebe8dfb138404397fb2c46732da6cd733a (diff) | |
download | cru-5dc738a57930271194bd86673eb86f149096a7b2.tar.gz cru-5dc738a57930271194bd86673eb86f149096a7b2.tar.bz2 cru-5dc738a57930271194bd86673eb86f149096a7b2.zip |
...
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/any_map.cpp | 30 | ||||
-rw-r--r-- | src/util/any_map.hpp | 94 | ||||
-rw-r--r-- | src/util/format.hpp | 94 | ||||
-rw-r--r-- | src/util/math_util.hpp | 51 | ||||
-rw-r--r-- | src/util/string_util.cpp | 21 | ||||
-rw-r--r-- | src/util/string_util.hpp | 8 |
6 files changed, 298 insertions, 0 deletions
diff --git a/src/util/any_map.cpp b/src/util/any_map.cpp new file mode 100644 index 00000000..c49464d3 --- /dev/null +++ b/src/util/any_map.cpp @@ -0,0 +1,30 @@ +#include "any_map.hpp" + +namespace cru::util { +AnyMap::ListenerToken AnyMap::RegisterValueChangeListener( + const String& key, const Listener& listener) { + const auto token = current_listener_token_++; + map_[key].second.push_back(token); + listeners_.emplace(token, listener); + return token; +} + +void AnyMap::UnregisterValueChangeListener(const ListenerToken token) { + const auto find_result = listeners_.find(token); + if (find_result != listeners_.cend()) listeners_.erase(find_result); +} + +void AnyMap::InvokeListeners(std::list<ListenerToken>& listener_list, + const std::any& value) { + auto i = listener_list.cbegin(); + while (i != listener_list.cend()) { + auto current_i = i++; + const auto find_result = listeners_.find(*current_i); + if (find_result != listeners_.cend()) + find_result->second(value); + else + listener_list.erase( + current_i); // otherwise remove the invalid listener token. + } +} +} // namespace cru::util diff --git a/src/util/any_map.hpp b/src/util/any_map.hpp new file mode 100644 index 00000000..d82167d2 --- /dev/null +++ b/src/util/any_map.hpp @@ -0,0 +1,94 @@ +#pragma once +#include "pre.hpp" + +#include <any> +#include <functional> +#include <optional> +#include <typeinfo> +#include <unordered_map> + +#include "base.hpp" +#include "format.hpp" + +namespace cru::util { +// 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<void(const std::any&)>; + + 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 <typename T> + std::optional<T> 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<T>(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 <typename T> + T GetValue(const String& key) const { + const auto optional_value = GetOptionalValue<T>(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 <typename T> + void SetValue(const String& key, T&& value) { + auto& p = map_[key]; + p.first = std::make_any<T>(std::forward<T>(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<ListenerToken>& listener_list, + const std::any& value); + + private: + std::unordered_map<String, std::pair<std::any, std::list<ListenerToken>>> + map_{}; + std::unordered_map<ListenerToken, Listener> listeners_{}; + ListenerToken current_listener_token_ = 0; +}; +} // namespace cru::util diff --git a/src/util/format.hpp b/src/util/format.hpp new file mode 100644 index 00000000..874c5b43 --- /dev/null +++ b/src/util/format.hpp @@ -0,0 +1,94 @@ +#pragma once +#include "pre.hpp" + +#include "base.hpp" + +namespace cru::util { +namespace details { +constexpr StringView PlaceHolder(type_tag<String>) { return StringView(L"{}"); } + +constexpr MultiByteStringView PlaceHolder(type_tag<MultiByteString>) { + return MultiByteStringView("{}"); +} + +template <typename TString> +void FormatInternal(TString& string) { + const auto find_result = string.find(PlaceHolder(type_tag<TString>{})); + if (find_result != TString::npos) + throw std::invalid_argument("There is more placeholders than args."); +} + +template <typename TString, typename T, typename... TRest> +void FormatInternal(TString& string, const T& arg, const TRest&... args) { + const auto find_result = string.find(PlaceHolder(type_tag<TString>{})); + if (find_result == TString::npos) + throw std::invalid_argument("There is less placeholders than args."); + + string.replace(find_result, 2, FormatToString(arg, type_tag<TString>{})); + FormatInternal<TString>(string, args...); +} +} // namespace details + +template <typename... T> +String Format(const StringView& format, const T&... args) { + String result(format); + details::FormatInternal<String>(result, args...); + return result; +} + +template <typename... T> +MultiByteString Format(const MultiByteStringView& format, const T&... args) { + MultiByteString result(format); + details::FormatInternal<MultiByteString>(result, args...); + return result; +} + +#define CRU_FORMAT_NUMBER(type) \ + inline String FormatToString(const type number, type_tag<String>) { \ + return std::to_wstring(number); \ + } \ + inline MultiByteString FormatToString(const type number, \ + type_tag<MultiByteString>) { \ + return std::to_string(number); \ + } + +CRU_FORMAT_NUMBER(int) +CRU_FORMAT_NUMBER(short) +CRU_FORMAT_NUMBER(long) +CRU_FORMAT_NUMBER(long long) +CRU_FORMAT_NUMBER(unsigned int) +CRU_FORMAT_NUMBER(unsigned short) +CRU_FORMAT_NUMBER(unsigned long) +CRU_FORMAT_NUMBER(unsigned long long) +CRU_FORMAT_NUMBER(float) +CRU_FORMAT_NUMBER(double) + +#undef CRU_FORMAT_NUMBER + +inline StringView FormatToString(const String& string, type_tag<String>) { + return string; +} + +inline MultiByteString FormatToString(const MultiByteString& string, + type_tag<MultiByteString>) { + return string; +} + +inline StringView FormatToString(const StringView& string, type_tag<String>) { + return string; +} + +inline MultiByteStringView FormatToString(const MultiByteStringView& string, + type_tag<MultiByteString>) { + return string; +} + +inline StringView FormatToString(const wchar_t* string, type_tag<String>) { + return StringView(string); +} + +inline MultiByteStringView FormatToString(const char* string, + type_tag<MultiByteString>) { + return MultiByteString(string); +} +} // namespace cru::util diff --git a/src/util/math_util.hpp b/src/util/math_util.hpp new file mode 100644 index 00000000..01348641 --- /dev/null +++ b/src/util/math_util.hpp @@ -0,0 +1,51 @@ +#pragma once +#include "pre.hpp" + +#include <optional> +#include <type_traits> + +namespace cru::util { +template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>> +float Coerce(const T n, const std::optional<T> min, + const std::optional<T> max) { + if (min.has_value() && n < min.value()) return min.value(); + if (max.has_value() && n > max.value()) return max.value(); + return n; +} + +template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>> +float Coerce(const T n, const T min, const T max) { + if (n < min) return min; + if (n > max) return max; + return n; +} + +template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>> +float Coerce(const T n, const std::nullopt_t, const std::optional<T> max) { + if (max.has_value() && n > max.value()) return max.value(); + return n; +} + +template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>> +float Coerce(const T n, const std::optional<T> min, const std::nullopt_t) { + if (min.has_value() && n < min.value()) return min.value(); + return n; +} + +template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>> +float Coerce(const T n, const std::nullopt_t, const T max) { + if (n > max) return max; + return n; +} + +template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>> +float Coerce(const T n, const T min, const std::nullopt_t) { + if (n < min) return min; + return n; +} + +template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>> +T AtLeast0(const T value) { + return value < static_cast<T>(0) ? static_cast<T>(0) : value; +} +} // namespace cru::util diff --git a/src/util/string_util.cpp b/src/util/string_util.cpp new file mode 100644 index 00000000..3c765259 --- /dev/null +++ b/src/util/string_util.cpp @@ -0,0 +1,21 @@ +#include "string_util.hpp" + +#include "system_headers.hpp" +#include "exception.hpp" + +namespace cru::util { +MultiByteString ToUtf8String(const StringView& string) { + if (string.empty()) return MultiByteString(); + + const auto length = ::WideCharToMultiByte(CP_UTF8, 0, string.data(), -1, + nullptr, 0, nullptr, nullptr); + MultiByteString result; + result.reserve(length); + if (::WideCharToMultiByte(CP_UTF8, 0, string.data(), -1, result.data(), + static_cast<int>(result.capacity()), nullptr, + nullptr) == 0) + throw Win32Error(::GetLastError(), + "Failed to convert wide string to UTF-8."); + return result; +} +} diff --git a/src/util/string_util.hpp b/src/util/string_util.hpp new file mode 100644 index 00000000..6d060089 --- /dev/null +++ b/src/util/string_util.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "pre.hpp" + +#include "base.hpp" + +namespace cru::util { +MultiByteString ToUtf8String(const StringView& string); +} |