Skip to content

Instantly share code, notes, and snippets.

@Nekrolm
Created May 30, 2023 22:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Nekrolm/1ffb240b4ca509d3be7a028229e10d52 to your computer and use it in GitHub Desktop.
Save Nekrolm/1ffb240b4ca509d3be7a028229e10d52 to your computer and use it in GitHub Desktop.
Magic handlers in C++20
#include <memory>
#include <type_traits>
#include <concepts>
#include <span>
#include <functional>
#include <iostream>
struct Context {
std::string_view body;
std::string_view headers;
};
// trait definition
// implementors have to specialize it with std::true_type & override methods
template <class T>
struct FromContextTrait : std::false_type {
static auto from_context(const Context&) -> T = delete;
};
template <class T>
concept FromContext = FromContextTrait<std::decay_t<T>>::value && requires (const Context& c){
{ FromContextTrait<std::decay_t<T>>::from_context(c) } -> std::same_as<std::decay_t<T>>;
};
template <class... A>
struct ArgsList {};
template <class F>
struct IsHandlerInvokeOperator : std::false_type {};
template <class C, class R, FromContext... Args>
struct IsHandlerInvokeOperator<R(C::*)(Args...)> : std::true_type {
using ArgsPack = ArgsList<Args...>;
};
template <class R, FromContext... Args>
struct IsHandlerInvokeOperator<R(*)(Args...)> : std::true_type {
using ArgsPack = ArgsList<Args...>;
};
template <class F>
auto handler_invoke_operator() {
if constexpr (requires {
F::operator();
}) {
return IsHandlerInvokeOperator<decltype(&F::operator())>{};
} else {
return std::false_type {};
}
}
template <class F>
struct HandlerTrait : decltype(handler_invoke_operator<F>()) {};
template <class R, FromContext... Args>
struct HandlerTrait<R(*)(Args...)> : std::true_type {
using ArgsPack = ArgsList<Args...>;
};
template <class R, FromContext... Args>
struct HandlerTrait<std::function<R(Args...)>> : std::true_type {
using ArgsPack = ArgsList<Args...>;
};
template <class F>
concept Handler = HandlerTrait<std::decay_t<F>>::value;
template <class... Args>
auto invoke_handler_impl(const Context& c, Handler auto&& h, ArgsList<Args...>) {
return h(FromContextTrait<std::decay_t<Args>>::from_context(c)...);
}
template <Handler H>
auto invoke_handler(const Context& c, H&& h) {
using Args = HandlerTrait<std::decay_t<H>>::ArgsPack;
return invoke_handler_impl(c, std::forward<H>(h), Args{});
}
struct Body {
std::string_view body;
};
template <>
struct FromContextTrait<Body> : std::true_type {
static auto from_context(const Context& c) -> Body {
return Body {
c.body
};
}
};
struct Header {
std::string_view header;
};
template <>
struct FromContextTrait<Header> : std::true_type {
static auto from_context(const Context& c) -> Header {
return Header {
c.headers
};
}
};
static auto handler_all(const Header& h, const Body& b) {
std::cout << __FUNCTION__ << " " << h.header << " " << b.body << "\n";
}
static auto handler_all_inverse(const Body& b, const Header& h) {
std::cout << __FUNCTION__ << " " << h.header << " " << b.body << "\n";
}
static auto handler_body(const Body& b) {
std::cout << __FUNCTION__ << " " << b.body << "\n";
}
static auto handler_header(const Header& h) {
std::cout << __FUNCTION__ << " " << h.header << "\n";
}
struct Engine {
template <Handler H>
auto register_handler(H&& h) {
this->handlers.push_back([h = std::forward<H>(h)](const Context& c) {
invoke_handler(c, h);
});
}
std::vector<std::function<void(const Context&)>> handlers;
void run(const Context& c) {
for (auto& h : this->handlers) {
h(c);
}
}
};
int main() {
Engine e {};
e.register_handler(handler_all);
e.register_handler(handler_all_inverse);
e.register_handler(handler_body);
e.register_handler(handler_header);
Context ctx {
"hello_world",
"world"
};
e.run(ctx);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment