On this page
article
peary/utils/dispatcher.hpp
peary/utils/dispatcher.hpp
Command dispatcher that takes function names and string arguments. More…
Namespaces
| Name |
|---|
| peary |
| peary::utils |
| peary::utils::dispatcher_impl |
Classes
| Name | |
|---|---|
| class | peary::utils::Dispatcher Command dispatcher class. |
| struct | peary::utils::dispatcher_impl::NativeInterfaceWrappper Wrapper for native interface functions that return a value. |
| struct | peary::utils::dispatcher_impl::NativeInterfaceWrappper< void, Args… > Wrapper for native interface functions that do not return a value. |
Detailed Description
Command dispatcher that takes function names and string arguments.
Copyright: Copyright (c) 2018 Moritz Kiehn. This software is distributed under the terms of the LGPL-3.0-only License, copied verbatim in the file “LICENSE.md”. SPDX-License-Identifier: MIT
Source code
#pragma once
#include <cassert>
#include <functional>
#include <sstream>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "peary/utils/exceptions.hpp"
namespace peary::utils {
class Dispatcher {
public:
using NativeInterface = std::function<std::string(const std::vector<std::string>&)>;
void add(std::string name, NativeInterface func, std::size_t nargs);
template <typename R, typename... Args> void add(std::string name, std::function<R(Args...)> func);
template <typename R, typename... Args> void add(std::string name, R (*func)(Args...));
template <typename T, typename R, typename... Args> void add(std::string name, R (T::*member_func)(Args...), T* t);
std::string call(const std::string& name, const std::vector<std::string>& args);
std::vector<std::pair<std::string, std::size_t>> commands() const;
private:
struct Command {
// The function that implements the command
NativeInterface func;
// The number of arguments that the command expects
std::size_t nargs;
};
std::unordered_map<std::string, Command> m_commands;
};
inline void Dispatcher::add(std::string name, Dispatcher::NativeInterface func, std::size_t nargs) {
if(name.empty()) {
throw InvalidArgumentError(__func__, "Can not register command with empty name");
}
if(m_commands.count(name) != 0u) {
throw utils::InvalidCommandError(name, "Command with this name already exists");
}
m_commands[std::move(name)] = Command {std::move(func), nargs};
}
namespace dispatcher_impl {
template <typename T> inline T str_decode(const std::string& str) {
T tmp;
std::istringstream is(str);
is >> tmp;
if(is.fail()) {
std::string msg;
msg += "Could not convert value '";
msg += str;
msg += "' to type '";
msg += typeid(T).name();
msg += "'";
throw InvalidArgumentError(__func__, msg);
}
return tmp;
}
template <> inline std::string str_decode(const std::string& str) {
return str;
}
template <typename T> inline std::string str_encode(const T& value) {
std::ostringstream os;
os << value;
if(os.fail()) {
std::string msg;
msg += "Could not convert type '";
msg += typeid(T).name();
msg += "' to std::string";
throw InvalidArgumentError(__func__, msg);
}
return os.str();
}
template <typename R, typename... Args> struct NativeInterfaceWrappper {
std::function<R(Args...)> func;
std::string operator()(const std::vector<std::string>& args) {
return decode_and_call(args, std::index_sequence_for<Args...> {});
}
template <std::size_t... I>
std::string decode_and_call(const std::vector<std::string>& args, std::index_sequence<I...>) {
return str_encode(func(str_decode<typename std::decay<Args>::type>(args.at(I))...));
}
};
template <typename... Args> struct NativeInterfaceWrappper<void, Args...> {
std::function<void(Args...)> func;
std::string operator()(const std::vector<std::string>& args) {
return decode_and_call(args, std::index_sequence_for<Args...> {});
}
template <std::size_t... I>
std::string decode_and_call(const std::vector<std::string>& args, std::index_sequence<I...>) {
func(str_decode<typename std::decay<Args>::type>(args.at(I))...);
return std::string();
}
};
template <typename R, typename... Args>
inline Dispatcher::NativeInterface make_native_interface(std::function<R(Args...)>&& function) {
return NativeInterfaceWrappper<R, Args...> {std::move(function)};
}
} // namespace dispatcher_impl
template <typename R, typename... Args> inline void Dispatcher::add(std::string name, std::function<R(Args...)> func) {
m_commands[std::move(name)] = Command {dispatcher_impl::make_native_interface(std::move(func)), sizeof...(Args)};
}
template <typename R, typename... Args> inline void Dispatcher::add(std::string name, R (*func)(Args...)) {
assert(func && "Function pointer must be non-null");
add(std::move(name), std::function<R(Args...)>(func));
}
template <typename T, typename R, typename... Args>
inline void Dispatcher::add(std::string name, R (T::*member_func)(Args...), T* t) {
assert(member_func && "Member function pointer must be non-null");
assert(t && "Object pointer must be non-null");
add(std::move(name), std::function<R(Args...)>([=](Args... args) { return (t->*member_func)(args...); }));
}
inline std::string Dispatcher::call(const std::string& name, const std::vector<std::string>& args) {
auto cmd = m_commands.find(name);
if(cmd == m_commands.end()) {
throw utils::InvalidCommandError(name, "Unknown command");
}
if(args.size() != cmd->second.nargs) {
throw InvalidArgumentError(__func__,
"Command '" + name + "' expects " + std::to_string(cmd->second.nargs) +
" arguments but " + std::to_string(args.size()) + " given");
}
return cmd->second.func(args);
}
inline std::vector<std::pair<std::string, std::size_t>> Dispatcher::commands() const {
std::vector<std::pair<std::string, std::size_t>> cmds;
for(const auto& cmd : m_commands) {
cmds.emplace_back(cmd.first, cmd.second.nargs);
}
return cmds;
}
} // namespace peary::utils
Updated on 2025-11-14 at 11:31:23 +0100