From e18a040e6ca1a6798affcd4731109855286d4c4c Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Sat, 12 Nov 2022 19:38:19 +0100 Subject: c++: add small test registry --- src/development/c++.md | 9 ++++ src/development/c++/Makefile | 10 +++++ src/development/c++/meta4.cc | 104 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 src/development/c++/Makefile create mode 100644 src/development/c++/meta4.cc diff --git a/src/development/c++.md b/src/development/c++.md index 1cc4003..e6fcfef 100644 --- a/src/development/c++.md +++ b/src/development/c++.md @@ -34,6 +34,15 @@ available traits an operation can have: {{#include c++/meta2.cc:3:}} ``` +## Example: Minimal templatized test registry + +A small test function registry bringing together a few different template +features. + +```cpp +{{#include c++/meta4.cc:3:}} +``` + ## Example: Concepts pre c++20 Prior to c++20's concepts, `SFINAE` and `std::void_t` can be leveraged to build diff --git a/src/development/c++/Makefile b/src/development/c++/Makefile new file mode 100644 index 0000000..610b5a2 --- /dev/null +++ b/src/development/c++/Makefile @@ -0,0 +1,10 @@ +SRC = concepts-11.cc meta.cc meta2.cc meta4.cc +BIN = $(SRC:.cc=) + +all: $(BIN) + +%: %.cc + $(CXX) -o $* $^ -std=c++17 -g -fsanitize=address -fsanitize=undefined -fsanitize=leak + +clean: + $(RM) $(BIN) 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 +#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; +} -- cgit v1.2.3