diff options
author | Johannes Stoelp <johannes.stoelp@gmail.com> | 2022-11-12 19:38:19 +0100 |
---|---|---|
committer | Johannes Stoelp <johannes.stoelp@gmail.com> | 2022-11-12 19:38:19 +0100 |
commit | e18a040e6ca1a6798affcd4731109855286d4c4c (patch) | |
tree | 9f5add03e686a4ee49d57cb6d25d93141cf50398 /src/development/c++/meta4.cc | |
parent | 71f731ca0675b19562221233e9a30ffcad0107d0 (diff) | |
download | notes-e18a040e6ca1a6798affcd4731109855286d4c4c.tar.gz notes-e18a040e6ca1a6798affcd4731109855286d4c4c.zip |
c++: add small test registry
Diffstat (limited to 'src/development/c++/meta4.cc')
-rw-r--r-- | src/development/c++/meta4.cc | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/src/development/c++/meta4.cc b/src/development/c++/meta4.cc new file mode 100644 index 0000000..9c3be77 --- /dev/null +++ b/src/development/c++/meta4.cc @@ -0,0 +1,104 @@ +// Copyright (C) 2022 johannst + +#include <cstdio> +#include <functional> +#include <map> +#include <string> +#include <type_traits> + +template<typename R, typename... P> +struct registry { + using FUNC = R (*)(P...); + using SELF = registry<R, P...>; + 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<R>(nm, p...); } + + void dump() const { + for (const auto& it : m_fns) { + std::puts(it.first.c_str()); + } + } + + private: + std::map<std::string, FUNC> m_fns; + + template<typename RET> + std::enable_if_t<std::is_same_v<RET, void>> 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<typename RET> + std::enable_if_t<!std::is_same_v<RET, void>, 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>, + "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<void>; +TEST(REG1, test1) { + std::puts("REG1::test1"); +} +TEST(REG1, test2) { + std::puts("REG1::test2"); +} + +// -- Usage 2 with convenience macro wrapper. + +using REG2 = registry<void, bool>; +#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; +} |