diff --git a/Universal/CMakeLists.txt b/Universal/CMakeLists.txt index 4202338..dc75abf 100644 --- a/Universal/CMakeLists.txt +++ b/Universal/CMakeLists.txt @@ -2,9 +2,16 @@ find_package(Boost REQUIRED COMPONENTS log log_setup) add_library(Universal BoostLog.h BoostLog.inl BoostLog.cpp + IoContext.h IoContext.cpp + MessageManager.h MessageManager.inl MessageManager.cpp + Singleton.h StreamFormat.h StreamFormat.inl StreamFormat.cpp ) +target_include_directories(Universal + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} +) + target_link_libraries(Universal - PRIVATE ${Boost_LIBRARIES} + PUBLIC ${Boost_LIBRARIES} ) \ No newline at end of file diff --git a/Universal/FunctionTraits.h b/Universal/FunctionTraits.h new file mode 100644 index 0000000..f425d7b --- /dev/null +++ b/Universal/FunctionTraits.h @@ -0,0 +1,128 @@ +#ifndef FUNCTIONTRAITS_H +#define FUNCTIONTRAITS_H + +#include +#include +#include + +template +class FunctionTraits; + +template +struct IsTuple : std::false_type { + enum { + num = 1, + }; +}; + +template +struct IsTuple> : std::true_type { + enum { + num = std::tuple_size>::value, + }; +}; + +template +struct IsTuple> : std::true_type { + enum { + num = std::tuple_size>::value, + }; +}; + +template +class FunctionTraits { +public: + enum { arity = sizeof...(Args), Results = IsTuple::num }; + using FunctionType = Ret(Args...); + using FunctionPointerType = FunctionType *; + using ReturnType = Ret; + using StlFunctionType = std::function; + using Arguments = std::tuple; + + template + class Argument { + public: + static_assert(I < arity, "index is out of range, index must less than sizeof Args..."); + using Type = std::tuple_element_t; + }; + + using FirstArgumentType = typename Argument<0>::Type; +}; + +template +class FunctionTraits { +public: + enum { arity = 0, Results = IsTuple::num }; + using FunctionType = Ret(); + using FunctionPointerType = FunctionType *; + using ReturnType = Ret; + using StlFunctionType = std::function; + using Arguments = std::tuple<>; + using FirstArgumentType = void; + template + class Argument { + public: + using Type = void; + }; +}; + +template <> +class FunctionTraits { +public: + using Arguments = std::tuple<>; + using FirstArgumentType = void; + template + class Argument { + public: + using Type = void; + }; +}; + +template +class FunctionTraits : public FunctionTraits {}; + +template +class FunctionTraits> : public FunctionTraits {}; + +template +class FunctionTraits : public FunctionTraits {}; + +template +class FunctionTraits : public FunctionTraits {}; + +template +class FunctionTraits : public FunctionTraits {}; + +template +class FunctionTraits : public FunctionTraits {}; + +// std::bind for object methods rclcpp/rclcpp/include/rclcpp/function_traits.hpp +template +#if defined _GLIBCXX_RELEASE // glibc++ (GNU C++ >= 7.1) +struct FunctionTraits> +#elif defined __GLIBCXX__ // glibc++ (GNU C++) +struct FunctionTraits(FArgs...)>> +#elif defined _MSC_VER // MS Visual Studio +struct FunctionTraits> +#elif defined __clang__ +struct FunctionTraits> +#else +#error "Unsupported C++ compiler / standard library" +#endif + : FunctionTraits { +}; + +// template +// class FunctionTraits> +// : public FunctionTraits::operator())> {}; + +template +class FunctionTraits : public FunctionTraits {}; + +template +typename FunctionTraits>::StlFunctionType makeStlFunction(Function &&lambda) { + return static_cast>::StlFunctionType>( + std::forward(lambda)); +} + +#endif // FUNCTIONTRAITS_H \ No newline at end of file diff --git a/Universal/IoContext.cpp b/Universal/IoContext.cpp new file mode 100644 index 0000000..d4b3656 --- /dev/null +++ b/Universal/IoContext.cpp @@ -0,0 +1,28 @@ +#include "IoContext.h" +#include +#include + +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 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(); + } +} diff --git a/Universal/IoContext.h b/Universal/IoContext.h new file mode 100644 index 0000000..aef0d62 --- /dev/null +++ b/Universal/IoContext.h @@ -0,0 +1,36 @@ +#ifndef IOCONTEXT_H +#define IOCONTEXT_H + +#include "Singleton.h" +#include +#include + +class IoContext { +public: + enum class Mode { + Synchronous, + Asynchronous, + }; + friend class Amass::Singleton; + std::shared_ptr ioContext() const { return m_ioContext; } + template + void run() { + if constexpr (mode == Mode::Asynchronous) { + m_thread = std::thread(&IoContext::runIoContext, this); + } else { + runIoContext(); + } + } + ~IoContext(); + +protected: + template + IoContext(Args &&... args) : m_ioContext(std::make_shared(std::forward(args)...)) {} + void runIoContext(); + +private: + std::thread m_thread; + std::shared_ptr m_ioContext; +}; + +#endif // IOCONTEXT_H diff --git a/Universal/MessageManager.cpp b/Universal/MessageManager.cpp new file mode 100644 index 0000000..bc46b89 --- /dev/null +++ b/Universal/MessageManager.cpp @@ -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 &¶meter) { + 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(); + } + } +} diff --git a/Universal/MessageManager.h b/Universal/MessageManager.h new file mode 100644 index 0000000..117f273 --- /dev/null +++ b/Universal/MessageManager.h @@ -0,0 +1,93 @@ +#ifndef MESSAGEMANAGER_H +#define MESSAGEMANAGER_H + +#include +#include +#include +#include +#include +#include +#include +#ifdef ANDROID +#include +#else +#include +#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; + using Executor = std::function; + +public: + template + 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 + void registerTopic(const std::string_view &topic, Function &&f); + + template + void registerTopic(const Message &topic, Function &&f); + + template + void removeTopic(const std::string_view &topic); + + template + size_t topicCount(const std::string_view &topic); + + template + void sendMessage(const std::string_view &topic, Args &&...args); + + template + void sendMessage(const std::string_view &topic, Args &&...args); + + template + void sendMessage(const Message &topic, Args &&...args); + + template + void sendAsyncMessage(const std::string_view &topic, Args &&...args); + + template + void sendAsyncMessage(const std::string_view &topic, Args &&...args); + + template + void sendAsyncMessage(const Message &topic, Args &&...args); + +protected: + MessageManager(const MessageManager &) = delete; + template + static Executor executorWrapper(Function &&f); + void callExecutor(const std::string &signature, std::any &¶meter); + void run(); + +private: + std::unordered_multimap m_executors; + std::queue m_params; + std::thread m_thread; + bool m_exit{false}; +}; + +#include "MessageManager.inl" + +#endif // MESSAGEMANAGER_H \ No newline at end of file diff --git a/Universal/MessageManager.inl b/Universal/MessageManager.inl new file mode 100644 index 0000000..5e96ae2 --- /dev/null +++ b/Universal/MessageManager.inl @@ -0,0 +1,153 @@ +#ifndef __MESSAGEMANAGER_INL__ +#define __MESSAGEMANAGER_INL__ + +#include "BoostLog.h" +#include "FunctionTraits.h" +#include "MessageManager.h" +#include +#include + +template +class Signature; + +template +class Signature> { +public: + using Type = std::tuple...>; +}; + +template +static void formatSignature(std::ostringstream &oss, Type &&type) { + oss << boost::typeindex::type_id(); +} + +template +static void formatSignature(std::ostringstream &oss, Type &&type, Args &&...args) { + oss << boost::typeindex::type_id() << ","; + formatSignature(oss, args...); +} + +template +static void formatSignature(std::ostringstream &oss, ArgTuple &&args, std::index_sequence) { + formatSignature(oss, std::get(args)...); +} + +template +static std::string formatTopicWithFunction(const std::string_view &topic) { + std::ostringstream oss; + using Arguments = typename FunctionTraits>::Arguments; + using Sigs = typename Signature::Type; + + oss << topic << "###["; + if constexpr (std::tuple_size::value > 0) { + formatSignature(oss, Sigs{}, std::make_index_sequence::value>{}); + } else { + oss << "void"; + } + oss << "]"; + return oss.str(); +} + +template +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)...); + } else { + oss << "void"; + } + + oss << "]"; + return oss.str(); +} + +template +void MessageManager::registerTopic(const std::string_view &topic, Function &&f) { + auto signature = formatTopicWithFunction(topic); + m_executors.emplace(signature, executorWrapper(std::move(f))); + LOG(debug) << "register message,topic: " << signature; +} + +template +void MessageManager::registerTopic(const Message &topic, Function &&f) { + using Arguments = typename FunctionTraits>::Arguments; + static_assert(std::is_same_v>, "arguments not same..."); + registerTopic(topic.topic, std::forward(f)); +} + +template +size_t MessageManager::topicCount(const std::string_view &topic) { + auto signature = formatTopicWithFunction(topic); + return m_executors.count(signature); +} + +template +void MessageManager::removeTopic(const std::string_view &topic) { + auto signature = formatTopicWithFunction(topic); + m_executors.erase(signature); +} + +template +void MessageManager::sendMessage(const std::string_view &topic, Args &&...args) { + auto signature = formatTopicWithParameters(topic, std::forward(args)...); + callExecutor(signature, std::make_tuple(std::forward(args)...)); +} + +template +void MessageManager::sendMessage(const std::string_view &topic, Args &&...args) { + using Arguments = typename FunctionTraits>::Arguments; + // LOG(debug) << boost::typeindex::type_id(); + auto signature = formatTopicWithFunction(topic); + callExecutor(signature, Arguments{std::forward(args)...}); +} + +template +void MessageManager::sendMessage(const Message &topic, Args &&...args) { + using Arguments = typename FunctionTraits::Arguments; + auto signature = formatTopicWithFunction(topic.topic); + callExecutor(signature, Arguments{std::forward(args)...}); +} + +template +void MessageManager::sendAsyncMessage(const std::string_view &topic, Args &&...args) { + auto signature = formatTopicWithParameters(topic, std::forward(args)...); + m_params.push({signature, std::make_tuple(std::forward(args)...)}); + LOG(debug) << "call async message,topic: " << signature; +} + +template +void MessageManager::sendAsyncMessage(const std::string_view &topic, Args &&...args) { + using Arguments = typename FunctionTraits>::Arguments; + auto signature = formatTopicWithFunction(topic); + m_params.push({signature, Arguments{std::forward(args)...}}); +} + +template +void MessageManager::sendAsyncMessage(const Message &topic, Args &&...args) { + using Function = void(TArgs...); + using Arguments = typename FunctionTraits::Arguments; + auto signature = formatTopicWithFunction(topic.topic); + m_params.push({signature, Arguments{std::forward(args)...}}); +} + +template +MessageManager::Executor MessageManager::executorWrapper(Function &&f) { + return [executor(std::move(f))](const std::any &args) -> bool { + using Arguments = typename FunctionTraits::Arguments; + auto arguments = std::any_cast(&args); + if (arguments != nullptr) { + std::apply(executor, *arguments); + return true; + } + using Signature = typename Signature::Type; + auto signature = std::any_cast(&args); + if (signature != nullptr) { + std::apply(executor, *signature); + return true; + } + return false; + }; +} + +#endif // __MESSAGEMANAGER_H__ diff --git a/Universal/Singleton.h b/Universal/Singleton.h new file mode 100644 index 0000000..5bf5b49 --- /dev/null +++ b/Universal/Singleton.h @@ -0,0 +1,82 @@ +#ifndef SINGLETON_H +#define SINGLETON_H + +#include "BoostLog.h" +#include +#include + +namespace Amass { + +using GlobalInstance = std::true_type; +using LocalInstance = std::false_type; + +struct Construct {}; +struct Reference {}; +struct Register {}; + +template +class Singleton; + +template +class Singleton { +public: + template + static std::shared_ptr instance(Args &&...args) { + // LOG(debug) << typeid(T).name() << " address: " << (size_t)&m_instance; + static_assert(std::is_base_of_v, "InstanceType must be child of T ..."); + if constexpr (std::is_same_v) { + if (!m_instance.expired()) { + throw std::runtime_error("an instance have constructed ..."); + } + auto instance = std::shared_ptr(new InstanceType(std::forward(args)...)); + m_instance = instance; + return instance; + } else if constexpr (std::is_same_v) { + return m_instance.expired() ? nullptr : m_instance.lock(); + } else if constexpr (std::is_same_v) { + 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, "Can not to it!"); + } + } + + static std::weak_ptr weakPointer() { + return m_instance; + } + +private: + Singleton() = delete; + ~Singleton() = delete; + Singleton(const Singleton &) = delete; + Singleton &operator=(const Singleton &) = delete; + +private: + inline static std::weak_ptr m_instance; +}; + +template +class Singleton { +public: + template + static T *instance(Args &&...args) { + if (!m_value) { + m_value.reset(new T(std::forward(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 m_value; +}; + +} // namespace Amass + +#endif // SINGLETON_H