From 2dfbc312e6ccb88f838170d8e777d48aacde2ff5 Mon Sep 17 00:00:00 2001 From: johannst Date: Sun, 13 Nov 2022 14:13:35 +0000 Subject: deploy: 026d679006e5d470bacdc74bb3082072edf31e36 --- development/c++.html | 108 ++++++++++++++++++++++++++++++++++++++++++++- development/c++/Makefile | 10 +++++ development/c++/meta4.cc | 104 +++++++++++++++++++++++++++++++++++++++++++ development/c++filt.html | 2 +- development/gcc.html | 2 +- development/glibc.html | 2 +- development/index.html | 2 +- development/ld.so.html | 2 +- development/make.html | 2 +- development/python.html | 2 +- development/symbolver.html | 2 +- 11 files changed, 229 insertions(+), 9 deletions(-) create mode 100644 development/c++/Makefile create mode 100644 development/c++/meta4.cc (limited to 'development') diff --git a/development/c++.html b/development/c++.html index 80d8ab5..c14cb2e 100644 --- a/development/c++.html +++ b/development/c++.html @@ -75,7 +75,7 @@ @@ -297,6 +297,112 @@ int main() { return 0; } +

Example: Minimal templatized test registry

+

A small test function registry bringing together a few different template +features.

+
#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;
+}
+

Example: Concepts pre c++20

Prior to c++20's concepts, SFINAE and std::void_t can be leveraged to build something similar allowing to define an interface (aka trait) for a template diff --git a/development/c++/Makefile b/development/c++/Makefile new file mode 100644 index 0000000..610b5a2 --- /dev/null +++ b/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/development/c++/meta4.cc b/development/c++/meta4.cc new file mode 100644 index 0000000..9c3be77 --- /dev/null +++ b/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; +} diff --git a/development/c++filt.html b/development/c++filt.html index 5b600a0..e3d7a0b 100644 --- a/development/c++filt.html +++ b/development/c++filt.html @@ -75,7 +75,7 @@

diff --git a/development/gcc.html b/development/gcc.html index 9ba42a9..ccf4351 100644 --- a/development/gcc.html +++ b/development/gcc.html @@ -75,7 +75,7 @@ diff --git a/development/glibc.html b/development/glibc.html index c0dd6a3..39a25bf 100644 --- a/development/glibc.html +++ b/development/glibc.html @@ -75,7 +75,7 @@ diff --git a/development/index.html b/development/index.html index 659d736..c05e92f 100644 --- a/development/index.html +++ b/development/index.html @@ -75,7 +75,7 @@ diff --git a/development/ld.so.html b/development/ld.so.html index 21c4da4..7f8dbf5 100644 --- a/development/ld.so.html +++ b/development/ld.so.html @@ -75,7 +75,7 @@ diff --git a/development/make.html b/development/make.html index b05b003..e822ec9 100644 --- a/development/make.html +++ b/development/make.html @@ -75,7 +75,7 @@ diff --git a/development/python.html b/development/python.html index 68e2a9d..b3da614 100644 --- a/development/python.html +++ b/development/python.html @@ -75,7 +75,7 @@ diff --git a/development/symbolver.html b/development/symbolver.html index 6273887..9142540 100644 --- a/development/symbolver.html +++ b/development/symbolver.html @@ -75,7 +75,7 @@ -- cgit v1.2.3