aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/development
diff options
context:
space:
mode:
authorJohannes Stoelp <johannes.stoelp@gmail.com>2022-11-12 19:38:19 +0100
committerJohannes Stoelp <johannes.stoelp@gmail.com>2022-11-12 19:38:19 +0100
commite18a040e6ca1a6798affcd4731109855286d4c4c (patch)
tree9f5add03e686a4ee49d57cb6d25d93141cf50398 /src/development
parent71f731ca0675b19562221233e9a30ffcad0107d0 (diff)
downloadnotes-e18a040e6ca1a6798affcd4731109855286d4c4c.tar.gz
notes-e18a040e6ca1a6798affcd4731109855286d4c4c.zip
c++: add small test registry
Diffstat (limited to 'src/development')
-rw-r--r--src/development/c++.md9
-rw-r--r--src/development/c++/Makefile10
-rw-r--r--src/development/c++/meta4.cc104
3 files changed, 123 insertions, 0 deletions
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 <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;
+}