Add class 'MessageManager'.

This commit is contained in:
luocai 2023-07-21 11:21:16 +08:00
parent ef8d5b0b03
commit bf5854addf
8 changed files with 565 additions and 1 deletions

View File

@ -2,9 +2,16 @@ find_package(Boost REQUIRED COMPONENTS log log_setup)
add_library(Universal add_library(Universal
BoostLog.h BoostLog.inl BoostLog.cpp BoostLog.h BoostLog.inl BoostLog.cpp
IoContext.h IoContext.cpp
MessageManager.h MessageManager.inl MessageManager.cpp
Singleton.h
StreamFormat.h StreamFormat.inl StreamFormat.cpp StreamFormat.h StreamFormat.inl StreamFormat.cpp
) )
target_include_directories(Universal
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(Universal target_link_libraries(Universal
PRIVATE ${Boost_LIBRARIES} PUBLIC ${Boost_LIBRARIES}
) )

128
Universal/FunctionTraits.h Normal file
View File

@ -0,0 +1,128 @@
#ifndef FUNCTIONTRAITS_H
#define FUNCTIONTRAITS_H
#include <functional>
#include <tuple>
#include <type_traits>
template <typename T>
class FunctionTraits;
template <typename>
struct IsTuple : std::false_type {
enum {
num = 1,
};
};
template <typename... T>
struct IsTuple<std::tuple<T...>> : std::true_type {
enum {
num = std::tuple_size<std::tuple<T...>>::value,
};
};
template <typename... T>
struct IsTuple<std::pair<T...>> : std::true_type {
enum {
num = std::tuple_size<std::pair<T...>>::value,
};
};
template <typename Ret, typename... Args>
class FunctionTraits<Ret(Args...)> {
public:
enum { arity = sizeof...(Args), Results = IsTuple<Ret>::num };
using FunctionType = Ret(Args...);
using FunctionPointerType = FunctionType *;
using ReturnType = Ret;
using StlFunctionType = std::function<FunctionType>;
using Arguments = std::tuple<Args...>;
template <size_t I>
class Argument {
public:
static_assert(I < arity, "index is out of range, index must less than sizeof Args...");
using Type = std::tuple_element_t<I, Arguments>;
};
using FirstArgumentType = typename Argument<0>::Type;
};
template <typename Ret>
class FunctionTraits<Ret()> {
public:
enum { arity = 0, Results = IsTuple<Ret>::num };
using FunctionType = Ret();
using FunctionPointerType = FunctionType *;
using ReturnType = Ret;
using StlFunctionType = std::function<FunctionType>;
using Arguments = std::tuple<>;
using FirstArgumentType = void;
template <size_t I>
class Argument {
public:
using Type = void;
};
};
template <>
class FunctionTraits<std::nullptr_t> {
public:
using Arguments = std::tuple<>;
using FirstArgumentType = void;
template <size_t I>
class Argument {
public:
using Type = void;
};
};
template <typename Ret, typename... Args>
class FunctionTraits<Ret (*)(Args...)> : public FunctionTraits<Ret(Args...)> {};
template <typename Ret, typename... Args>
class FunctionTraits<std::function<Ret(Args...)>> : public FunctionTraits<Ret(Args...)> {};
template <typename Ret, typename ClassType, typename... Args>
class FunctionTraits<Ret (ClassType::*)(Args...)> : public FunctionTraits<Ret(Args...)> {};
template <typename Ret, typename ClassType, typename... Args>
class FunctionTraits<Ret (ClassType::*)(Args...) volatile> : public FunctionTraits<Ret(Args...)> {};
template <typename Ret, typename ClassType, typename... Args>
class FunctionTraits<Ret (ClassType::*)(Args...) const> : public FunctionTraits<Ret(Args...)> {};
template <typename Ret, typename ClassType, typename... Args>
class FunctionTraits<Ret (ClassType::*)(Args...) const volatile> : public FunctionTraits<Ret(Args...)> {};
// std::bind for object methods rclcpp/rclcpp/include/rclcpp/function_traits.hpp
template <typename ClassT, typename ReturnTypeT, typename... Args, typename... FArgs>
#if defined _GLIBCXX_RELEASE // glibc++ (GNU C++ >= 7.1)
struct FunctionTraits<std::_Bind<ReturnTypeT (ClassT::*(FArgs...))(Args...)>>
#elif defined __GLIBCXX__ // glibc++ (GNU C++)
struct FunctionTraits<std::_Bind<std::_Mem_fn<ReturnTypeT (ClassT::*)(Args...)>(FArgs...)>>
#elif defined _MSC_VER // MS Visual Studio
struct FunctionTraits<std::_Binder<std::_Unforced, ReturnTypeT (ClassT::*)(Args...), FArgs...>>
#elif defined __clang__
struct FunctionTraits<std::__bind<ReturnTypeT (ClassT::*(FArgs...))(Args...)>>
#else
#error "Unsupported C++ compiler / standard library"
#endif
: FunctionTraits<ReturnTypeT(Args...)> {
};
// template <typename ClassT, typename ReturnTypeT, typename... Args, typename... FArgs>
// class FunctionTraits<std::_Bind<ReturnTypeT (ClassT::*(FArgs...))(Args...)>>
// : public FunctionTraits<decltype(std::_Bind<ReturnTypeT (ClassT::*(FArgs...))(Args...)>::operator())> {};
template <typename Callable>
class FunctionTraits : public FunctionTraits<decltype(&Callable::operator())> {};
template <typename Function>
typename FunctionTraits<std::decay_t<Function>>::StlFunctionType makeStlFunction(Function &&lambda) {
return static_cast<typename FunctionTraits<std::decay_t<Function>>::StlFunctionType>(
std::forward<Function>(lambda));
}
#endif // FUNCTIONTRAITS_H

28
Universal/IoContext.cpp Normal file
View File

@ -0,0 +1,28 @@
#include "IoContext.h"
#include <boost/asio/executor_work_guard.hpp>
#include <boost/scope_exit.hpp>
IoContext::~IoContext() {
m_ioContext->stop();
if (m_thread.joinable()) m_thread.join();
}
void IoContext::runIoContext() {
LOG(info) << "asio context started ...";
BOOST_SCOPE_EXIT(void) {
LOG(info) << "asio context exited ...";
}
BOOST_SCOPE_EXIT_END
try {
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work(m_ioContext->get_executor());
m_ioContext->run();
} catch (const boost::log::system_error &error) {
LOG(error) << error.what();
} catch (const std::out_of_range &e) {
LOG(error) << e.what();
} catch (const boost::exception &e) {
LOG(error) << boost::diagnostic_information(e);
} catch (const std::exception &e) {
LOG(error) << e.what();
}
}

36
Universal/IoContext.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef IOCONTEXT_H
#define IOCONTEXT_H
#include "Singleton.h"
#include <boost/asio/io_context.hpp>
#include <thread>
class IoContext {
public:
enum class Mode {
Synchronous,
Asynchronous,
};
friend class Amass::Singleton<IoContext, Amass::LocalInstance>;
std::shared_ptr<boost::asio::io_context> ioContext() const { return m_ioContext; }
template <Mode mode = Mode::Asynchronous>
void run() {
if constexpr (mode == Mode::Asynchronous) {
m_thread = std::thread(&IoContext::runIoContext, this);
} else {
runIoContext();
}
}
~IoContext();
protected:
template <typename... Args>
IoContext(Args &&... args) : m_ioContext(std::make_shared<boost::asio::io_context>(std::forward<Args>(args)...)) {}
void runIoContext();
private:
std::thread m_thread;
std::shared_ptr<boost::asio::io_context> m_ioContext;
};
#endif // IOCONTEXT_H

View File

@ -0,0 +1,37 @@
#include "MessageManager.h"
MessageManager::MessageManager() {
m_thread = std::thread(&MessageManager::run, this);
}
MessageManager::~MessageManager() {
m_exit = true;
if (m_thread.joinable()) m_thread.join();
}
void MessageManager::run() {
LOG(trace) << "message runner[" << std::this_thread::get_id() << "] start...";
while (!m_exit) {
if (m_params.empty()) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
continue;
}
auto &[topic, args] = m_params.front();
callExecutor(topic, std::move(args));
m_params.pop();
}
LOG(trace) << "message runner exit...";
}
void MessageManager::callExecutor(const std::string &signature, std::any &&parameter) {
auto executors = m_executors.equal_range(signature);
for (auto executor = executors.first; executor != executors.second; ++executor) {
try {
if (!executor->second(parameter)) {
LOG(warning) << "topic: " << signature << " execute failed, parameter cast failed.";
}
} catch (const std::exception &e) {
LOG(error) << e.what();
}
}
}

View File

@ -0,0 +1,93 @@
#ifndef MESSAGEMANAGER_H
#define MESSAGEMANAGER_H
#include <functional>
#include <queue>
#include <string>
#include <string_view>
#include <thread>
#include <type_traits>
#include <unordered_map>
#ifdef ANDROID
#include <boost/any.hpp>
#else
#include <any>
#endif
class MessageManager {
#ifdef ANDROID
using ParamEntry = boost::any;
using BadEntryCast = boost::bad_any_cast;
#else
using ParamEntry = std::any;
using BadEntryCast = std::bad_any_cast;
#endif
using AsyncParam = std::pair<std::string, ParamEntry>;
using Executor = std::function<bool(const ParamEntry &)>;
public:
template <typename... Args>
class Message {
public:
explicit constexpr Message(const char *topic) : topic(topic) {
}
std::string_view topic;
};
MessageManager();
~MessageManager();
/**
* @brief std::bind() is not recommended,recommended to use lambda. if you must use std::bind(),do not use
* placeholders!
*
* @tparam Function
* @param topic
* @param f
*/
template <typename Function>
void registerTopic(const std::string_view &topic, Function &&f);
template <typename Function, typename... Args>
void registerTopic(const Message<Args...> &topic, Function &&f);
template <typename Function>
void removeTopic(const std::string_view &topic);
template <typename Function>
size_t topicCount(const std::string_view &topic);
template <typename... Args>
void sendMessage(const std::string_view &topic, Args &&...args);
template <typename Function, typename... Args>
void sendMessage(const std::string_view &topic, Args &&...args);
template <typename... TArgs, typename... Args>
void sendMessage(const Message<TArgs...> &topic, Args &&...args);
template <typename... Args>
void sendAsyncMessage(const std::string_view &topic, Args &&...args);
template <typename Function, typename... Args>
void sendAsyncMessage(const std::string_view &topic, Args &&...args);
template <typename... TArgs, typename... Args>
void sendAsyncMessage(const Message<TArgs...> &topic, Args &&...args);
protected:
MessageManager(const MessageManager &) = delete;
template <typename Function>
static Executor executorWrapper(Function &&f);
void callExecutor(const std::string &signature, std::any &&parameter);
void run();
private:
std::unordered_multimap<std::string, Executor> m_executors;
std::queue<AsyncParam> m_params;
std::thread m_thread;
bool m_exit{false};
};
#include "MessageManager.inl"
#endif // MESSAGEMANAGER_H

View File

@ -0,0 +1,153 @@
#ifndef __MESSAGEMANAGER_INL__
#define __MESSAGEMANAGER_INL__
#include "BoostLog.h"
#include "FunctionTraits.h"
#include "MessageManager.h"
#include <boost/type_index.hpp>
#include <sstream>
template <typename Args>
class Signature;
template <typename... Args>
class Signature<std::tuple<Args...>> {
public:
using Type = std::tuple<std::decay_t<Args>...>;
};
template <typename Type>
static void formatSignature(std::ostringstream &oss, Type &&type) {
oss << boost::typeindex::type_id<Type>();
}
template <typename Type, typename... Args>
static void formatSignature(std::ostringstream &oss, Type &&type, Args &&...args) {
oss << boost::typeindex::type_id<Type>() << ",";
formatSignature(oss, args...);
}
template <typename ArgTuple, std::size_t... I>
static void formatSignature(std::ostringstream &oss, ArgTuple &&args, std::index_sequence<I...>) {
formatSignature(oss, std::get<I>(args)...);
}
template <typename Function>
static std::string formatTopicWithFunction(const std::string_view &topic) {
std::ostringstream oss;
using Arguments = typename FunctionTraits<std::decay_t<Function>>::Arguments;
using Sigs = typename Signature<Arguments>::Type;
oss << topic << "###[";
if constexpr (std::tuple_size<Arguments>::value > 0) {
formatSignature<Arguments>(oss, Sigs{}, std::make_index_sequence<std::tuple_size<Arguments>::value>{});
} else {
oss << "void";
}
oss << "]";
return oss.str();
}
template <typename... Args>
static std::string formatTopicWithParameters(const std::string_view &topic, Args &&...args) {
std::ostringstream oss;
oss << topic << "###[";
if constexpr (sizeof...(Args) > 0) {
formatSignature(oss, std::forward<Args>(args)...);
} else {
oss << "void";
}
oss << "]";
return oss.str();
}
template <typename Function>
void MessageManager::registerTopic(const std::string_view &topic, Function &&f) {
auto signature = formatTopicWithFunction<Function>(topic);
m_executors.emplace(signature, executorWrapper(std::move(f)));
LOG(debug) << "register message,topic: " << signature;
}
template <typename Function, typename... Args>
void MessageManager::registerTopic(const Message<Args...> &topic, Function &&f) {
using Arguments = typename FunctionTraits<std::decay_t<Function>>::Arguments;
static_assert(std::is_same_v<Arguments, std::tuple<Args...>>, "arguments not same...");
registerTopic(topic.topic, std::forward<Function>(f));
}
template <typename Function>
size_t MessageManager::topicCount(const std::string_view &topic) {
auto signature = formatTopicWithFunction<Function>(topic);
return m_executors.count(signature);
}
template <typename Function>
void MessageManager::removeTopic(const std::string_view &topic) {
auto signature = formatTopicWithFunction<Function>(topic);
m_executors.erase(signature);
}
template <typename... Args>
void MessageManager::sendMessage(const std::string_view &topic, Args &&...args) {
auto signature = formatTopicWithParameters(topic, std::forward<Args>(args)...);
callExecutor(signature, std::make_tuple(std::forward<Args>(args)...));
}
template <typename Function, typename... Args>
void MessageManager::sendMessage(const std::string_view &topic, Args &&...args) {
using Arguments = typename FunctionTraits<std::decay_t<Function>>::Arguments;
// LOG(debug) << boost::typeindex::type_id<Arguments>();
auto signature = formatTopicWithFunction<Function>(topic);
callExecutor(signature, Arguments{std::forward<Args>(args)...});
}
template <typename... TArgs, typename... Args>
void MessageManager::sendMessage(const Message<TArgs...> &topic, Args &&...args) {
using Arguments = typename FunctionTraits<void(TArgs...)>::Arguments;
auto signature = formatTopicWithFunction<void(TArgs...)>(topic.topic);
callExecutor(signature, Arguments{std::forward<TArgs>(args)...});
}
template <typename... Args>
void MessageManager::sendAsyncMessage(const std::string_view &topic, Args &&...args) {
auto signature = formatTopicWithParameters(topic, std::forward<Args>(args)...);
m_params.push({signature, std::make_tuple(std::forward<Args>(args)...)});
LOG(debug) << "call async message,topic: " << signature;
}
template <typename Function, typename... Args>
void MessageManager::sendAsyncMessage(const std::string_view &topic, Args &&...args) {
using Arguments = typename FunctionTraits<std::decay_t<Function>>::Arguments;
auto signature = formatTopicWithFunction<Function>(topic);
m_params.push({signature, Arguments{std::forward<Args>(args)...}});
}
template <typename... TArgs, typename... Args>
void MessageManager::sendAsyncMessage(const Message<TArgs...> &topic, Args &&...args) {
using Function = void(TArgs...);
using Arguments = typename FunctionTraits<Function>::Arguments;
auto signature = formatTopicWithFunction<Function>(topic.topic);
m_params.push({signature, Arguments{std::forward<TArgs>(args)...}});
}
template <typename Function>
MessageManager::Executor MessageManager::executorWrapper(Function &&f) {
return [executor(std::move(f))](const std::any &args) -> bool {
using Arguments = typename FunctionTraits<Function>::Arguments;
auto arguments = std::any_cast<Arguments>(&args);
if (arguments != nullptr) {
std::apply(executor, *arguments);
return true;
}
using Signature = typename Signature<Arguments>::Type;
auto signature = std::any_cast<Signature>(&args);
if (signature != nullptr) {
std::apply(executor, *signature);
return true;
}
return false;
};
}
#endif // __MESSAGEMANAGER_H__

82
Universal/Singleton.h Normal file
View File

@ -0,0 +1,82 @@
#ifndef SINGLETON_H
#define SINGLETON_H
#include "BoostLog.h"
#include <boost/assert.hpp>
#include <memory>
namespace Amass {
using GlobalInstance = std::true_type;
using LocalInstance = std::false_type;
struct Construct {};
struct Reference {};
struct Register {};
template <typename T, typename Style = LocalInstance>
class Singleton;
template <typename T>
class Singleton<T, LocalInstance> {
public:
template <typename Invoke = Reference, typename InstanceType = T, typename... Args>
static std::shared_ptr<T> instance(Args &&...args) {
// LOG(debug) << typeid(T).name() << " address: " << (size_t)&m_instance;
static_assert(std::is_base_of_v<T, InstanceType>, "InstanceType must be child of T ...");
if constexpr (std::is_same_v<Invoke, Construct>) {
if (!m_instance.expired()) {
throw std::runtime_error("an instance have constructed ...");
}
auto instance = std::shared_ptr<T>(new InstanceType(std::forward<Args>(args)...));
m_instance = instance;
return instance;
} else if constexpr (std::is_same_v<Invoke, Reference>) {
return m_instance.expired() ? nullptr : m_instance.lock();
} else if constexpr (std::is_same_v<Invoke, Register>) {
static_assert(sizeof...(args) == 1, "args must equal to 1...");
m_instance = std::get<0>(std::forward_as_tuple(args)...);
return std::get<0>(std::forward_as_tuple(args)...);
} else {
static_assert(!std::is_same_v<Invoke, GlobalInstance>, "Can not to it!");
}
}
static std::weak_ptr<T> weakPointer() {
return m_instance;
}
private:
Singleton() = delete;
~Singleton() = delete;
Singleton(const Singleton &) = delete;
Singleton &operator=(const Singleton &) = delete;
private:
inline static std::weak_ptr<T> m_instance;
};
template <typename T>
class Singleton<T, GlobalInstance> {
public:
template <typename... Args>
static T *instance(Args &&...args) {
if (!m_value) {
m_value.reset(new T(std::forward<Args>(args)...));
}
return m_value.get();
}
private:
Singleton() = delete;
~Singleton() = delete;
Singleton(const Singleton &) = delete;
Singleton &operator=(const Singleton &) = delete;
private:
inline static std::shared_ptr<T> m_value;
};
} // namespace Amass
#endif // SINGLETON_H