Last active
August 2, 2022 03:38
-
-
Save ghtz08/5c4c59cde849cd363004da6454a53a09 to your computer and use it in GitHub Desktop.
注册器,可自定义 Key
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <memory> | |
#include <stdexcept> | |
#include <string> | |
#include <string> | |
#include <tuple> | |
#include <type_traits> | |
#include <unordered_map> | |
#define let auto const | |
// 约束 | |
namespace registry_details { | |
template <typename T, typename = void> | |
struct ToStr { | |
static_assert(sizeof(T) == -1, "Not implemented into string"); | |
}; | |
template <> | |
struct ToStr<std::string, void> { | |
auto operator()(std::string val) const -> std::string { | |
return std::move(val); | |
} | |
}; | |
template <> | |
struct ToStr<std::string_view, void> { | |
auto operator()(std::string_view val) const -> std::string { | |
return static_cast<std::string>(val); | |
} | |
}; | |
template <typename T> | |
struct ToStr<T, std::enable_if_t<std::is_integral_v<T>>> { | |
auto operator()(T val) const -> std::string { | |
return std::to_string(val); | |
} | |
}; | |
template <typename T> | |
struct ToStr<T, std::enable_if_t<std::is_enum_v<T>>> { | |
auto operator()(T val) const -> std::string { | |
let val_i = static_cast<std::underlying_type_t<T>>(val); | |
return ToStr<std::remove_cv_t<std::remove_reference_t<decltype(val_i)>>>()(val_i); | |
} | |
}; | |
} // namespace registry_details | |
template <typename T> | |
auto registry_key_into_str(T val) -> std::string { | |
return registry_details::ToStr<T>()(val); | |
} | |
template <typename AKey, typename AVal, typename AMaker, typename... AConsArgs> | |
class RegistryEx { | |
public: | |
using Key = AKey; | |
using Val = AVal; | |
using Maker = AMaker; | |
using ConsArgs = std::tuple<AConsArgs...>; | |
private: | |
using Reg = std::unordered_map<Key, Maker>; | |
public: | |
auto register_maker(Key key, Maker maker) -> void { | |
// TODO: decltype Maker(ConsArgs) = std::unique_ptr<Val> | |
if (!registry_.try_emplace(key, std::move(maker)).second) { | |
throw std::runtime_error("duplicate key: " + registry_key_into_str(key)); | |
} | |
} | |
template <typename T> | |
auto register_type(Key key) -> void { | |
static_assert(std::is_base_of_v<Val, T>); | |
static_assert(std::has_virtual_destructor_v<Val>); | |
auto maker = [](AConsArgs... args) { | |
auto val = new T(std::forward<AConsArgs>(args)...); | |
return std::unique_ptr<Val>(val); | |
}; | |
this->register_maker(std::move(key), static_cast<Maker>(std::move(maker))); | |
} | |
template <typename... Args> | |
auto make(Key const & key, Args &&... args) const -> std::unique_ptr<Val> { | |
static_assert(sizeof...(Args) == sizeof...(AConsArgs)); | |
static_assert((... && std::is_convertible_v<Args&&, AConsArgs>)); | |
let iter = registry_.find(key); | |
if (iter == registry_.cend()) { | |
auto err = std::string("unknown key: " + registry_key_into_str(key) + ", optional: ["); | |
auto first = true; | |
for (let & [k, v] : registry_) { | |
err.append(first ? " " : ", "); | |
err.append(registry_key_into_str(k)); | |
first = false; | |
} | |
err.append(first ? "]" : " ]"); | |
throw std::out_of_range(err); | |
} | |
return (iter->second)(std::forward<Args>(args)...); | |
} | |
auto begin() const noexcept -> typename Reg::const_iterator { | |
return this->registry_.cbegin(); | |
} | |
auto end() const noexcept -> typename Reg::const_iterator { | |
return this->registry_.cend(); | |
} | |
private: | |
Reg registry_; | |
}; // template class RegistryEx | |
template <typename Key, typename Val, typename... ConsArgs> | |
struct RegistryK: RegistryEx<Key, Val, std::unique_ptr<Val>(*)(ConsArgs...), ConsArgs...> { | |
}; | |
template <typename Val, typename... ConsArgs> | |
struct Registry: RegistryK<std::string_view, Val, ConsArgs...> { | |
}; // template class Registry | |
#include <cassert> | |
enum class Key { | |
add, sub, mul | |
}; | |
template <> | |
auto registry_key_into_str<Key>(Key val) -> std::string { | |
switch (val) { | |
case Key::add: return "e_add"; | |
case Key::sub: return "e_sub"; | |
case Key::mul: return "e_mul"; | |
default: { | |
assert(false); | |
return "none"; | |
} | |
} | |
} | |
auto test() -> void { | |
{ | |
auto registry = Registry<double, float, int>(); | |
registry.register_maker("add", [](float a, int b) { | |
return std::make_unique<double>(a + b); | |
}); | |
registry.register_maker("sub", [](float a, int b) { | |
return std::make_unique<double>(a - b); | |
}); | |
try { | |
registry.register_maker("sub", [](float a, int b) { | |
return std::make_unique<double>(a - b); | |
}); | |
assert(false); | |
} | |
catch (std::runtime_error const& e) { | |
std::cout << e.what() << "\n"; | |
} | |
std::cout << "3+4=" << *registry.make("add", 3.0f, 4) << "\n"; | |
std::cout << "3-4=" << *registry.make("sub", 3.0f, 4) << "\n"; | |
try { | |
std::cout << *registry.make("mul", 3.0f, 4) << "\n"; | |
assert(false); | |
} | |
catch (std::out_of_range const& e) { | |
std::cout << e.what() << "\n"; | |
} | |
std::cout << "registry: [ "; | |
for (let & [key, maker] : registry) { | |
std::cout << (key == registry.begin()->first ? "": ", ") << key; | |
} | |
std::cout << " ]\n"; | |
} | |
{ | |
auto registry = RegistryK<Key, double, float, int>(); | |
registry.register_maker(Key::add, [](float a, int b) { | |
return std::make_unique<double>(a + b); | |
}); | |
registry.register_maker(Key::sub, [](float a, int b) { | |
return std::make_unique<double>(a - b); | |
}); | |
try { | |
registry.register_maker(Key::sub, [](float a, int b) { | |
return std::make_unique<double>(a - b); | |
}); | |
assert(false); | |
} | |
catch (std::runtime_error const& e) { | |
std::cout << e.what() << "\n"; | |
} | |
std::cout << "3+4=" << *registry.make(Key::add, 3.0f, 4) << "\n"; | |
std::cout << "3-4=" << *registry.make(Key::sub, 3.0f, 4) << "\n"; | |
try { | |
std::cout << *registry.make(Key::mul, 3.0f, 4) << "\n"; | |
assert(false); | |
} | |
catch (std::out_of_range const& e) { | |
std::cout << e.what() << "\n"; | |
} | |
} | |
} | |
int main() { | |
test(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment