Created
May 30, 2023 22:37
-
-
Save Nekrolm/1ffb240b4ca509d3be7a028229e10d52 to your computer and use it in GitHub Desktop.
Magic handlers in C++20
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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