// Copyright (C) 2022 johannst #include #include #include #include #include template struct registry { using FUNC = R (*)(P...); using SELF = registry; using RET = R; static SELF& get() { static SELF r; return r; } bool add(std::string nm, FUNC fn) { const auto r = m_fns.insert({std::move(nm), std::move(fn)}); return r.second; } R invoke(const std::string& nm, P... p) const { return invoke_impl(nm, p...); } void dump() const { for (const auto& it : m_fns) { std::puts(it.first.c_str()); } } private: std::map m_fns; template std::enable_if_t> invoke_impl(const std::string& nm, P... p) const { const auto it = m_fns.find(nm); if (it == m_fns.end()) { return; } std::invoke(it->second, p...); } template std::enable_if_t, RET> invoke_impl(const std::string& nm, P... p) const { const auto it = m_fns.find(nm); if (it == m_fns.end()) { static_assert(std::is_default_constructible_v, "RET must be default constructible"); return {}; } return std::invoke(it->second, p...); } }; #define TEST_REGISTER(REGISTRY, NAME) \ static bool regfn_##REGISTRY##NAME() { \ const bool r = REGISTRY::get().add(#NAME, NAME); \ if (!r) { \ std::puts("Failed to register test " #NAME ", same name already registered!"); \ std::abort(); \ } \ return r; \ } \ static const bool reg_##REGISTRY##NAME = regfn_##REGISTRY##NAME(); #define TEST(REGISTRY, NAME, ...) \ REGISTRY::RET NAME(__VA_ARGS__); \ TEST_REGISTER(REGISTRY, NAME); \ REGISTRY::RET NAME(__VA_ARGS__) // -- Usage 1 simple usage. using REG1 = registry; TEST(REG1, test1) { std::puts("REG1::test1"); } TEST(REG1, test2) { std::puts("REG1::test2"); } // -- Usage 2 with convenience macro wrapper. using REG2 = registry; #define TEST2(NAME, ...) TEST(REG2, NAME, ##__VA_ARGS__) TEST2(test1, bool val) { printf("REG2::test1 val %d\n", val); } int main() { const auto& R1 = REG1::get(); R1.dump(); R1.invoke("test1"); R1.invoke("test2"); const auto& R2 = REG2::get(); R2.dump(); R2.invoke("test1", true); return 0; }