patterncppMinor
Mapping a textual method name to a function
Viewed 0 times
methodfunctiontextualnamemapping
Problem
My program (a daemon hosting a Minecraft server) exposes a D-Bus interface to allow interaction with the server from other processes. To that end, and to avoid a chain of repetitive conditionals in the invocation handler, I'm using a map of
I realise that, for the current set of methods, I could just as well use a map of
Glib::ustring keys (the method invoked) to std::bind-created function objects (code abbreviated slightly; see end of question for link to full source):std::unordered_map&)>> handlerMap;
std::once_flag handlerMapInitFlag;
Minecraftd::Minecraftd1() {
// Only initialise the method name/handler method map once. Placeholders are used for the "this" and invocation arguments.
std::call_once(handlerMapInitFlag, []{
using namespace std::placeholders;
handlerMap.emplace("SaveAll", std::bind(&Minecraftd1::handleSimpleCommand, _1, _2, "save-all"));
// Omitted: two similar mappings
handlerMap.emplace("Stop", std::bind(&Minecraftd1::handleSimpleCommand, _1, _2, "stop"));
});
}
// Called by the Glib main loop when a D-Bus method call is received
void Minecraftd1::onMethodCall(const Glib::ustring &methodName, const Glib::RefPtr &invocation) {
auto handler = handlerMap.find(methodName);
if(handler != handlerMap.end()) {
handler->second(this, invocation);
} else {
invocation->return_error(Gio::DBus::Error{Gio::DBus::Error::UNKNOWN_METHOD, "Method does not exist."});
}
}
void Minecraftd1::handleSimpleCommand(const Glib::RefPtr &invocation, std::string command) const {
// Write value of command to standard input (rebound to a pipe)
invocation->return_value(Glib::VariantContainerBase{});
}I realise that, for the current set of methods, I could just as well use a map of
methodName to the command string to write to the file descriptor, but this way of doing it allows me to create more complex handlers for commands that require more than just a simple pass-through without having to change the execution flow of onMethodCall. Of course, if a handler Solution
std::call_once is an interesting tool, but there are alternatives built into the language: namely, you can use the Immediately-Invoked Function Expression (IIFE) borrowed straight from JavaScript and made usable thanks to C++11 lambda functions.Here is how you can use it to initialize
handlerMap:std::unordered_map&)>
> handlerMap = []{
using namespace std::placeholders;
std::unordered_map&)>
> initMap;
initMap.emplace("SaveAll", std::bind(&Minecraftd1::handleSimpleCommand, _1, _2, "save-all"));
initMap.emplace("Stop", std::bind(&Minecraftd1::handleSimpleCommand, _1, _2, "stop"));
return initMap;
}();Agreed, it looks a bit messy with the big
std::unordered_map type, but you could probably create a type alias to get rid of the syntactic mess. Even if you dont typedef the whole thing, I guess that exposing the type of std::function&)> could be an interesting idea.Alterntively, there is even better than IIFE: in your can, you can use initializer lists to achieve what you're trying to do:
std::unordered_map&)>
> handlerMap = {
{ "SaveAll", std::bind(&Minecraftd1::handleSimpleCommand, std::placeholders::_1, std::placeholders::_2, "save-all") },
{ "Stop", std::bind(&Minecraftd1::handleSimpleCommand, std::placeholders::_1, std::placeholders::_2, "stop") }
};But it means that you have to explicitly write
std::placeholders:: everywhere if you don't want using std::placeholders; to leak into the outer namespace. In other words, you have to choose which solutions seems to best convey your idea while not bearing too much syntactic noise.Code Snippets
std::unordered_map<
Glib::ustring,
std::function<void(Minecraftd1*, const Glib::RefPtr<Gio::DBus::MethodInvocation>&)>
> handlerMap = []{
using namespace std::placeholders;
std::unordered_map<
Glib::ustring,
std::function<void(Minecraftd1*, const Glib::RefPtr<Gio::DBus::MethodInvocation>&)>
> initMap;
initMap.emplace("SaveAll", std::bind(&Minecraftd1::handleSimpleCommand, _1, _2, "save-all"));
initMap.emplace("Stop", std::bind(&Minecraftd1::handleSimpleCommand, _1, _2, "stop"));
return initMap;
}();std::unordered_map<
Glib::ustring,
std::function<void(Minecraftd1*, const Glib::RefPtr<Gio::DBus::MethodInvocation>&)>
> handlerMap = {
{ "SaveAll", std::bind(&Minecraftd1::handleSimpleCommand, std::placeholders::_1, std::placeholders::_2, "save-all") },
{ "Stop", std::bind(&Minecraftd1::handleSimpleCommand, std::placeholders::_1, std::placeholders::_2, "stop") }
};Context
StackExchange Code Review Q#73966, answer score: 3
Revisions (0)
No revisions yet.