Reputation: 16476
I was reading a blog post on the Anthony Williams website when I somehow wandered onto an example of his just::thread library, his barber shop example.
In it he has a series of a structures that doesn't inherit from anything:
struct start_haircut {};
struct no_room {};
struct shop_closed {};
He then has a receive
function that he chains the .match()
template to:
jss::actor::receive()
.match<start_haircut>(
[&](start_haircut){
//...
})
.match<no_room>(
[&](no_room){
//...
})
.match<shop_closed>(
[&](shop_closed)
{
//...
});
The receive function returns an unspecified_message_receiver
object that specifies the type (shop_closed
, etc.) and the lambda handler.
What goes inside the receive
and match
functions? How does the receive
and match
functions interact?
This is an interesting pattern that can have applications outside the threading model it is used on. I am interested in it for communications over tcp between sensors where small message packets and small amounts of data are being transferred continuously.
Upvotes: 1
Views: 162
Reputation: 873
Here is one way to achieve it, be wary though as it uses runtime info instead of compile time information to achieve this, which may not be the way that the C++ template magician would achieve the solution :)!
godbolt link: https://godbolt.org/g/yZC8sM
You can try this online using ideone.
#include <typeinfo>
#include <map>
#include <memory>
#include <functional>
#include <iostream>
struct Erased {
virtual ~Erased(){};
};
template <typename T>
struct Handler : public Erased {
std::function<void(T)> handl;
};
struct cont {
template<typename T>
void handle(T msg) {
const std::type_info *t = &typeid(T);
auto iter = handlers.find(t);
if(iter != handlers.end()) {
dynamic_cast<Handler<T>*>(iter->second.get())->handl(msg);
}
}
template <typename T>
void match(std::function<void (T)> handler) {
auto realHandler = std::make_shared<Handler<T>>();
realHandler->handl = handler;
const std::type_info* t = &typeid(T);
handlers.insert(std::make_pair(t, realHandler));
}
std::map<const std::type_info*, std::shared_ptr<Erased>> handlers;
};
int main() {
cont container;
container.match<int>([](int x) { std::cout << " got avalue of ... " << x; });
container.handle(5);
}
Just wanted to fill in on my answer here:
What i've written above is a solution using the runtime data to do this dispatch ( much simpler to understand), however you can create such dispatch using compile time dispatchers without std::map at all. small notes how -
You need to create a special type for each chain that is:
.match([](int&) { ...}).match([](double&) {. .. });
where each match would create a complete new type with multiple methods, then using the type erasure you will get a generic type on which you can do a virtual dispatch to that erased type - and then using template generated code invoke your desired handler.
Upvotes: 0
Reputation: 66441
This looks (unsurprisingly) like Erlang.
This is pretty clearly described in the documentation you linked to, and quoted.
The receive function returns an
unspecified_message_receiver
object
so jss::actor::receive()
is an unspecified_message_receiver
,
Calling
match()
on a receiver adds the specifiedMsgType
to the list of handled messages, and registers the specified handler to be called when a message of that type is received.
so
.match<start_haircut>(
[&](start_haircut){
//...
})
registers the lambda to handle messages of type start_haircut
, in the receiver returned previously.
Since each match
returns a message receiver, you can chain them to register more handlers.
I'm not sure what else can be said to clarify, but a more realistic use might employ some types that carry some kind of payload, such as
struct start_haircut { enum { Long, Short, Shaved } style; };
jss::actor::receive()
.match<start_haircut>(
[&](start_haircut cut){
switch (cut.style)
{
case start_haircut::Long:
// ...
}
})
.match<no_room>(
[&](no_room){
//...
})
.match<shop_closed>(
[&](shop_closed)
{
//...
});
(This kind of interface probably makes more sense if you take a peek at an Erlang tutorial, like "Learn you some Erlang for great good!").
Upvotes: 1