Skip to content

Instantly share code, notes, and snippets.

@ghtz08
Last active August 2, 2022 03:38
Show Gist options
  • Save ghtz08/5c4c59cde849cd363004da6454a53a09 to your computer and use it in GitHub Desktop.
Save ghtz08/5c4c59cde849cd363004da6454a53a09 to your computer and use it in GitHub Desktop.
注册器,可自定义 Key
#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