Info
-
Did you know about C++ allows to pass Pointer To Member Function via template parameter?
Example
struct header { int type{}; };
int main() {
std::cout << []<auto Ptr>(const auto& msg) {
return msg.*Ptr;
}.operator()<&header::type>(header{42}); // prints 42
}
Puzzle
- Can you implement a
dispatch
lambda expression which calls::dispatch
function based onheader::type
andmsg::id
?
struct [[gnu::packed]] header {
int type{};
};
struct [[gnu::packed]] msg1 : header {
static constexpr auto id = 1;
int i{};
};
struct [[gnu::packed]] msg2 : header {
static constexpr auto id = 2;
float f{};
};
auto dispatch(std::ostream& out, const msg1& msg) { out << "msg1: " << msg.i; }
auto dispatch(std::ostream& out, const msg2& msg) { out << "msg2: " << msg.f; }
#include <https://raw.githubusercontent.com/boost-ext/ut/v1.1.9/include/boost/ut.hpp>
int main() {
using namespace boost::ut;
constexpr auto dispatch = /* TODO */;
"pointer to member dispatch not found"_test = [dispatch] {
const auto msg = msg1{};
const void* buffer = &msg;
std::stringstream out{};
dispatch.operator()<&header::type, msg1, msg2>(out, buffer);
expect(std::empty(out.str()));
};
"pointer to member dispatch found msg1"_test = [dispatch] {
const auto msg = msg1{msg1::id, 42};
const void* buffer = &msg;
std::stringstream out{};
dispatch.operator()<&header::type, msg1, msg2>(out, buffer);
expect(out.str() == std::string_view{"msg1: 42"});
};
"pointer to member dispatch found msg2"_test = [dispatch] {
const auto msg = msg2{msg2::id, 4.2};
const void* buffer = &msg;
std::stringstream out{};
dispatch.operator()<&header::type, msg1, msg2>(out, buffer);
expect(out.str() == std::string_view{"msg2: 4.2"});
};
}
Solutions
constexpr auto dispatch = []<auto Type, typename... Msgs>(
auto& out, const auto& buffer) {
const auto type = static_cast<const header*>(buffer)->*Type;
(
[&]() {
switch (type) {
case Msgs::id:
const auto& msg = *static_cast<const Msgs*>(buffer);
::dispatch(out, msg);
break;
}
}(),
...);
};
constexpr auto dispatch = []<auto pm, class... Ts>(std::ostream& out,
const void* buffer) {
const auto type = static_cast<const header*>(buffer)->*pm;
std::variant<std::monostate, Ts...> v;
(
[&]() {
if (type == Ts::id) {
v = *static_cast<const Ts*>(buffer);
}
}(),
...);
return std::visit(overload{[&](auto&& msg) { ::dispatch(out, msg); },
[](std::monostate) {}},
v);
};
constexpr auto dispatch = []<auto Ptr, class... MessageTypes>(
auto& out, auto buffer) -> void {
const auto helper = [&]<class M>(const M* m) {
if (m->*Ptr == M::id) {
::dispatch(out, (M)(*reinterpret_cast<const M*>(buffer)));
}
};
(helper(reinterpret_cast<const MessageTypes*>(buffer)), ...);
};
constexpr auto dispatch = [&]<auto Ptr, class... Ts>(std::stringstream& out,
const void* buffer) {
((reinterpret_cast<const header*>(buffer)->*Ptr == Ts::id &&
(::dispatch(out, *reinterpret_cast<const Ts*>(buffer)), true)) ||
...);
};