C++实现简化版Qt的QObject(4):增加简单实用的事件机制

前面的文章已经实现了许多QObject的功能了:
C++实现一个简单的Qt信号槽机制
C++实现简化版Qt信号槽机制(2):增加内存安全保障
C++实现简化版Qt的QObject(3):增加父子关系、属性系统

但是,Qt中还有一个关键的功能是事件系统。

为了让我们的QObject也支持事件系统,我们可以设计一套简单实用的事件机制。

设计事件循环

事件系统离不开事件循环,截止到C++ 20标准库里没有一套官方的事件循环机制,可能是因为实现一个事件循环也不是什么难事吧。

为了简化我们的事件循环机制,我们用到了一个c++11引入的独特的容器:priority_queue 。它提供了一个严格的弱序列,其中每个元素都有一个优先级。在 priority_queue 中,元素被按优先级排序,最高优先级的元素总是位于队列的前端。我们将事件循环的时间按执行时间点的远近作为优先级,可以很好地简化排序逻辑。

结合boost::asio和qt的EventLoop的使用经验,我计划这个简单实用的事件循环使用的姿势如下:

refl::CEventLoop loop;
loop.post([]() {std::cout << "Immediate task\n";});//马上执行loop.post([]() {std::cout << "Delayed task\n";}, std::chrono::seconds(1));//延时一秒std::thread loop_thread([&loop]() {loop.run();});std::this_thread::sleep_for(std::chrono::seconds(2));
loop.stop();//停止消息循环
loop_thread.join();

实现事件循环

在确定使用方法之后,总体的接口和功能实现就比较简单了。
核心成员变量和函数功能注释如下:

class CEventLoop {std::priority_queue<TimedHandler> tasks_;//成员变量:任务队列std::mutex mutex_;//用于等待相关std::condition_variable cond_;//用于等待相关std::atomic<bool> running_{ true };//标识是否正在运行// "post" 函数用于提交一个待执行的任务到事件循环中。// 参数 "handler" 是一个函数对象,代表需要异步执行的任务。// 参数 "delay" 表示任务延迟执行的时间,默认是立即执行(Duration::zero())。void post(Handler handler, Duration delay = Duration::zero()) {// 对互斥量上锁,保证线程安全。std::unique_lock<std::mutex> lock(mutex_);// 将任务和它应该被执行的时间点(现在 + 延迟)一起存入优先级队列中。tasks_.push({Clock::now() + delay, std::move(handler)});// 通知一个等待中的线程(如果有的话),有新的任务已经被提交。cond_.notify_one();}// "run" 函数启动事件循环,循环内部不断地执行任务。void run() {// 只要 "running_" 标志为 true,事件循环就会继续运行。while (running_) {// 对互斥量上锁,保证线程安全。std::unique_lock<std::mutex> lock(mutex_);// 如果当前没有任务可执行,就等待直到有新任务被提交或者事件循环被停止。if (tasks_.empty()) {cond_.wait(lock, [this] { return !tasks_.empty() || !running_; });}// 当有任务可以执行时(优先级队列中最早的任务时间 <= 当前时间),执行它们。while (!tasks_.empty() && tasks_.top().first <= Clock::now()) {// 从队列中取出任务。auto task = std::move(tasks_.top());// 将任务从队列中移除。tasks_.pop();// 释放互斥量锁,以便其他线程可以提交任务或者修改任务队列。lock.unlock();// 执行任务。task.second();// 任务执行完毕后,再次上锁互斥量。lock.lock();}// 如果队列中还有任务,等待直到队列中最早的任务到达执行时间。if (!tasks_.empty()) {// 等待直到最早任务的执行时间,或者条件变量被通知。cond_.wait_until(lock, tasks_.top().first);}}}
};

虽然几十行代码实现的时间循环非常精简,但是也不能少了扩展能力,通过扩展可以实现更复杂的主消息循环等复杂场景。于是我们引入一个回调类IEventLoopHost:

		class IEventLoopHost {public:virtual void onWaitForTask(std::condition_variable& cond, std::unique_lock<std::mutex>& locker) = 0;virtual void onEvent(TimedHandler& event) = 0;virtual void onWaitForRun(std::condition_variable& cond, std::unique_lock<std::mutex>& locker, const TimePoint& timePoint) = 0;};

在特定时机可以通过回调类替换掉默认实现,从而实现完整的功能扩展。

事件循环的完整代码

后续放到github上迭代(https://github.com/kevinyangli/simple_qt_qobject.git)

class CEventLoop {
public:using Clock = std::chrono::steady_clock;using TimePoint = Clock::time_point;using Duration = Clock::duration;using Handler = std::function<void()>;struct TimedHandler {TimePoint time;Handler handler;bool operator<(const TimedHandler& other) const {return time > other.time;}};class IEventLoopHost {public:virtual void onWaitForTask(std::condition_variable& cond, std::unique_lock<std::mutex>& locker) = 0;virtual void onEvent(TimedHandler& event) = 0;virtual void onWaitForRun(std::condition_variable& cond, std::unique_lock<std::mutex>& locker, const TimePoint& timePoint) = 0;};
private:IEventLoopHost* host = nullptr;std::priority_queue<TimedHandler> tasks_;std::mutex mutex_;std::condition_variable cond_;std::atomic<bool> running_{ true };public:void setHost(IEventLoopHost* host) {this->host = host;}void post(Handler handler, Duration delay = Duration::zero()) {std::unique_lock<std::mutex> lock(mutex_);tasks_.push({ Clock::now() + delay, std::move(handler) });cond_.notify_one();}void run() {while (running_) {std::unique_lock<std::mutex> lock(mutex_);if (tasks_.empty()) {if (host) {host->onWaitForTask(cond_, lock);}else {cond_.wait(lock, [this] { return !tasks_.empty() || !running_; });}}while (!tasks_.empty() && tasks_.top().time <= Clock::now()) {auto task = tasks_.top();tasks_.pop();lock.unlock();if (host) {host->onEvent(task);}else {task.handler();}lock.lock();}if (!tasks_.empty()) {if (host) {host->onWaitForRun(cond_, lock, tasks_.top().time);}else {cond_.wait_until(lock, tasks_.top().time);}}}}void stop() {running_ = false;cond_.notify_all();}};

带上完整的测试代码,以及之前的功能实现:

#include <iostream>
#include <tuple>
#include <stdexcept>
#include <assert.h>
#include <string_view>
#include <optional>
#include <utility> // For std::forward
#include <unordered_map>
#include <functional>
#include <memory>
#include <any>
#include <type_traits> // For std::is_invocable
#include <map>#include <chrono>
#include <thread>
#include <vector>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <atomic>namespace refl {// 这个宏用于创建字段信息
#define REFLECTABLE_PROPERTIES(TypeName, ...)  using CURRENT_TYPE_NAME = TypeName; \static constexpr auto properties_() { return std::make_tuple(__VA_ARGS__); }
#define REFLECTABLE_MENBER_FUNCS(TypeName, ...) using CURRENT_TYPE_NAME = TypeName; \static constexpr auto member_funcs() { return std::make_tuple(__VA_ARGS__); }// 这个宏用于创建属性信息,并自动将字段名转换为字符串
#define REFLEC_PROPERTY(Name) refl::Property<decltype(&CURRENT_TYPE_NAME::Name), &CURRENT_TYPE_NAME::Name>(#Name)
#define REFLEC_FUNCTION(Func) refl::Function<decltype(&CURRENT_TYPE_NAME::Func), &CURRENT_TYPE_NAME::Func>(#Func)// 定义一个属性结构体,存储字段名称和值的指针template <typename T, T Value>struct Property {const char* name;constexpr Property(const char* name) : name(name) {}constexpr T get_value() const { return Value; }};template <typename T, T Value>struct Function {const char* name;constexpr Function(const char* name) : name(name) {}constexpr T get_func() const { return Value; }};// 使用 std::any 来处理不同类型的字段值和函数返回值template <typename T, typename Tuple, size_t N = 0>std::any __get_field_value_impl(T& obj, const char* name, const Tuple& tp) {if constexpr (N >= std::tuple_size_v<Tuple>) {return std::any();// Not Found!}else {const auto& prop = std::get<N>(tp);if (std::string_view(prop.name) == name) {return std::any(obj.*(prop.get_value()));}else {return __get_field_value_impl<T, Tuple, N + 1>(obj, name, tp);}}}// 使用 std::any 来处理不同类型的字段值和函数返回值template <typename T, size_t N = 0>std::any get_field_value(T* obj, const char* name) {return obj ? __get_field_value_impl(*obj, name, T::properties_()) : std::any();}// 使用 std::any 来处理不同类型的字段值和函数返回值template <typename T, typename Tuple, typename Value, size_t N = 0>std::any __assign_field_value_impl(T& obj, const char* name, const Value& value, const Tuple& tp) {if constexpr (N >= std::tuple_size_v<Tuple>) {return std::any();// Not Found!}else {const auto& prop = std::get<N>(tp);if (std::string_view(prop.name) == name) {if constexpr (std::is_assignable_v<decltype(obj.*(prop.get_value())), Value>) {obj.*(prop.get_value()) = value;return std::any(obj.*(prop.get_value()));}else {assert(false);// 无法赋值 类型不匹配!!return std::any();}}else {return __assign_field_value_impl<T, Tuple, Value, N + 1>(obj, name, value, tp);}}}template <typename T, typename Value>std::any assign_field_value(T* obj, const char* name, const Value& value) {return obj ? __assign_field_value_impl(*obj, name, value, T::properties_()) : std::any();}// 成员函数调用相关:template <bool assert_when_error = true, typename T, typename FuncTuple, size_t N = 0, typename... Args>constexpr std::any __invoke_member_func_impl(T& obj, const char* name, const FuncTuple& tp, Args&&... args) {if constexpr (N >= std::tuple_size_v<FuncTuple>) {assert(!assert_when_error);// 没找到!return std::any();// Not Found!}else {const auto& func = std::get<N>(tp);if (std::string_view(func.name) == name) {if constexpr (std::is_invocable_v<decltype(func.get_func()), T&, Args...>) {if constexpr (std::is_void<decltype(std::invoke(func.get_func(), obj, std::forward<Args>(args)...))>::value) {// 如果函数返回空,那么兼容这种casestd::invoke(func.get_func(), obj, std::forward<Args>(args)...);return std::any();}else {return std::invoke(func.get_func(), obj, std::forward<Args>(args)...);}}else {assert(!assert_when_error);// 调用参数不匹配return std::any();}}else {return __invoke_member_func_impl<assert_when_error, T, FuncTuple, N + 1>(obj, name, tp, std::forward<Args>(args)...);}}}template <typename T, typename... Args>constexpr std::any invoke_member_func(T* obj, const char* name, Args&&... args) {constexpr auto funcs = T::member_funcs();return obj ? __invoke_member_func_impl(obj, name, funcs, std::forward<Args>(args)...) : std::any();}template <typename T, typename... Args>constexpr std::any invoke_member_func_safe(T* obj, const char* name, Args&&... args) {constexpr auto funcs = T::member_funcs();return obj ? __invoke_member_func_impl<true>(obj, name, funcs, std::forward<Args>(args)...) : std::any();}template <typename T, typename FuncPtr, typename FuncTuple, size_t N = 0>constexpr const char* __get_member_func_name_impl(FuncPtr func_ptr, const FuncTuple& tp) {if constexpr (N >= std::tuple_size_v<FuncTuple>) {return nullptr; // Not Found!}else {const auto& func = std::get<N>(tp);if constexpr (std::is_same< decltype(func.get_func()), FuncPtr >::value) {return func.name;}else {return __get_member_func_name_impl<T, FuncPtr, FuncTuple, N + 1>(func_ptr, tp);}}}template <typename T, typename FuncPtr>constexpr const char* get_member_func_name(FuncPtr func_ptr) {constexpr auto funcs = T::member_funcs();return __get_member_func_name_impl<T, FuncPtr>(func_ptr, funcs);}// 定义一个类型特征模板,用于获取属性信息template <typename T>struct For {static_assert(std::is_class_v<T>, "Reflector requires a class type.");// 遍历所有字段名称template <typename Func>static void for_each_propertie_name(Func&& func) {constexpr auto props = T::properties_();std::apply([&](auto... x) {((func(x.name)), ...);}, props);}// 遍历所有字段值template <typename Func>static void for_each_propertie_value(T* obj, Func&& func) {constexpr auto props = T::properties_();std::apply([&](auto... x) {((func(x.name, obj->*(x.get_value()))), ...);}, props);}// 遍历所有函数名称template <typename Func>static void for_each_member_func_name(Func&& func) {constexpr auto props = T::member_funcs();std::apply([&](auto... x) {((func(x.name)), ...);}, props);}};// ===============================================================// 以下是动态反射机制的支持代码:namespace dynamic {// 反射基类class IReflectable : public std::enable_shared_from_this<IReflectable> {public:virtual ~IReflectable() = default;virtual std::string_view get_type_name() const = 0;virtual std::any get_field_value_by_name(const char* name) const = 0;virtual std::any invoke_member_func_by_name(const char* name) = 0;virtual std::any invoke_member_func_by_name(const char* name, std::any param1) = 0;virtual std::any invoke_member_func_by_name(const char* name, std::any param1, std::any param2) = 0;virtual std::any invoke_member_func_by_name(const char* name, std::any param1, std::any param2, std::any param3) = 0;virtual std::any invoke_member_func_by_name(const char* name, std::any param1, std::any param2, std::any param3, std::any param4) = 0;// 不能无限增加,会增加虚表大小。最多支持4个参数的调用。};// 类型注册工具class TypeRegistry {public:using CreatorFunc = std::function<std::shared_ptr<IReflectable>()>;static TypeRegistry& instance() {static TypeRegistry registry;return registry;}void register_type(const std::string_view type_name, CreatorFunc creator) {creators_[type_name] = std::move(creator);}std::shared_ptr<IReflectable> create(const std::string_view type_name) {if (auto it = creators_.find(type_name); it != creators_.end()) {return it->second();}return nullptr;}private:std::unordered_map<std::string_view, CreatorFunc> creators_;};// 用于注册类型信息的宏
#define DECL_DYNAMIC_REFLECTABLE(TypeName) \friend class refl::dynamic::TypeRegistryEntry<TypeName>; \static std::string_view static_type_name() { return #TypeName; } \virtual std::string_view get_type_name() const override { return static_type_name(); } \static std::shared_ptr<::refl::dynamic::IReflectable> create_instance() { return std::make_shared<TypeName>(); } \static const bool is_registered; \std::any get_field_value_by_name(const char* name) const override { \return refl::get_field_value(this, name); \} \std::any invoke_member_func_by_name(const char* name) override { \return refl::invoke_member_func(static_cast<TypeName*>(this), name); \}\std::any invoke_member_func_by_name(const char* name, std::any param1) override { \return refl::invoke_member_func(static_cast<TypeName*>(this), name, param1); \}\std::any invoke_member_func_by_name(const char* name, std::any param1, std::any param2) override { \return refl::invoke_member_func(static_cast<TypeName*>(this), name, param1, param2); \}\std::any invoke_member_func_by_name(const char* name, std::any param1, std::any param2, std::any param3) override { \return refl::invoke_member_func(static_cast<TypeName*>(this), name, param1, param2, param3); \}\std::any invoke_member_func_by_name(const char* name, std::any param1, std::any param2, std::any param3, std::any param4) override { \return refl::invoke_member_func(static_cast<TypeName*>(this), name, param1, param2, param3, param4); \}\
// 用于在静态区域注册类型的辅助类template <typename T>class TypeRegistryEntry {public:TypeRegistryEntry() {::refl::dynamic::TypeRegistry::instance().register_type(T::static_type_name(), &T::create_instance);}};// 为每个类型定义注册变量,这段宏需要出现在cpp中。
#define REGEDIT_DYNAMIC_REFLECTABLE(TypeName) \const bool TypeName::is_registered = [] { \static ::refl::dynamic::TypeRegistryEntry<TypeName> entry; \return true; \}();}//namespace dynamic//宏用于类中声明信号,并提供一个同名的方法来触发信号。宏参数是函数参数列表。示例:/*	void x_value_modified(int param) {IMPL_SIGNAL(param);}*/
#define REFLEC_IMPL_SIGNAL(...) raw_emit_signal_impl(__func__ , __VA_ARGS__)class CObject :public refl::dynamic::IReflectable {private:// 信号与槽的映射,键是信号名称,值是一组槽函数的信息using connections_list_type = std::list<std::tuple< std::weak_ptr<IReflectable>, std::string>>;using connections_type = std::unordered_map<std::string, connections_list_type>;connections_type connections_;public:template<typename... Args>void raw_emit_signal_impl(const char* signal_name, Args&&... args) {auto it = connections_.find(signal_name);if (it != connections_.end()) {auto& slots = it->second; // 获取槽信息列表的引用bool has_invalid_slot = false;for (const auto& slot_info : slots) {auto ptr = std::get<0>(slot_info).lock(); // 锁定弱引用if (ptr) {ptr->invoke_member_func_by_name(std::get<1>(slot_info).c_str(), std::forward<Args>(args)...);}else {has_invalid_slot = true;}}if (has_invalid_slot) {//如果存在无效对象,则执行一轮移除操作auto remove_it = std::remove_if(slots.begin(), slots.end(),[](const auto& slot_info) {return std::get<0>(slot_info).expired(); // 检查弱引用是否失效});slots.erase(remove_it, slots.end());}}else {/*没找到这个信号,要不要assert?*/ }}auto connect(const char* signal_name, refl::CObject* slot_instance, const char* slot_member_func_name) {if (!slot_instance || !signal_name || !slot_member_func_name) {throw std::runtime_error("param is null!");}assert(slot_instance->weak_from_this().lock());//target必须通过make_share构造!!因为要弱引用它std::string str_signal_name(signal_name);auto itMap = connections_.find(str_signal_name);if (itMap != connections_.end()) {itMap->second.emplace_back(slot_instance->weak_from_this(), slot_member_func_name);//必须插入末尾,因为返回了--end()迭代器指示这个链接return std::make_optional(std::make_tuple(this, itMap, --itMap->second.end()));}else {// 如果没找到,插入新元素到map中,并获取迭代器auto emplace_result = connections_.emplace(std::make_pair(std::move(str_signal_name), connections_list_type()));itMap = emplace_result.first;itMap->second.emplace_back(slot_instance->weak_from_this(), slot_member_func_name);return std::make_optional(std::make_tuple(this, itMap, --itMap->second.end()));}}template <typename SlotClass>auto connect(const char* signal_name, std::shared_ptr<SlotClass> slot_instance, const char* slot_member_func_name) {return connect(signal_name, slot_instance.get(), slot_member_func_name);}template <typename SignalClass, typename SignalType, typename SlotClass, typename SlotType>auto connect(SignalType SignalClass::* signal, SlotClass* slot_instance, SlotType SlotClass::* slot) {const char* signal_name = get_member_func_name<SignalClass>(signal);const char* slot_name = get_member_func_name<SlotClass>(slot);if (signal_name && slot_name) {return connect(signal_name, static_cast<CObject*>(slot_instance), slot_name);}throw std::runtime_error("signal name or slot_name is not found!");}template <typename SignalClass, typename SignalType, typename SlotClass, typename SlotType>auto connect(SignalType SignalClass::* signal, std::shared_ptr<SlotClass>& slot_instance, SlotType SlotClass::* slot) {return connect(signal, slot_instance.get(), slot);}template <typename T>bool disconnect(T connection) {//T是个这个类型:std::make_optional(std::make_tuple(this, itMap, it)); 由于T过于复杂,就直接用模板算了if (!connection) {return false;}auto& tuple = connection.value();if (std::get<0>(tuple) != this) {return false;//不是我的connection呀}std::get<1>(tuple)->second.erase(std::get<2>(tuple));return true;}};class QObject : public CObject {private:std::string objectName_;std::weak_ptr<refl::dynamic::IReflectable> parent_;std::unordered_map<std::string, std::any> properties_;std::list<std::shared_ptr<refl::dynamic::IReflectable>> children_;public:void setObjectName(const char* name) {objectName_ = name;}const std::string& getObjectName() {return objectName_;}void setParent(QObject* newParent) {if (auto oldParent = dynamic_cast<QObject*>(parent_.lock().get())) {auto it = std::find_if(oldParent->children_.begin(), oldParent->children_.end(),[this](const auto& child) { return child.get() == this; });if (it != oldParent->children_.end()) {oldParent->children_.erase(it);}}if (newParent) {parent_ = newParent->weak_from_this();newParent->children_.push_back(shared_from_this());}else {parent_.reset();}}template <typename T>void setParent(std::shared_ptr<T> newParent) {setParent(newParent.get());}void removeChild(CObject* child) {auto ch = static_cast<refl::dynamic::IReflectable*>(child);auto it = std::find_if(children_.begin(), children_.end(),[this, ch](const auto& child) { return child.get() == ch; });if (it != children_.end()) {children_.erase(it);}}CObject* findChild(const char* name) {for (auto child : children_) {QObject* qChild = dynamic_cast<QObject*>(child.get());if (qChild && qChild->objectName_ == name) {return qChild;}}return nullptr;}CObject* findChildRecursively(const char* name) {for (auto child : children_) {QObject* qChild = dynamic_cast<QObject*>(child.get());if (qChild) {if (qChild->objectName_ == name) {return qChild;}CObject* found = qChild->findChildRecursively(name);if (found) {return found;}}}return nullptr;}const std::any& getProperty(const char* name) {return properties_[name];}template<typename T>const T* getProperty(const char* name) {try {return &std::any_cast<const T&>(properties_[name]);}catch (...) {return nullptr;}}void setProperty(const char* name, const std::any& value) {properties_[name] = value;}};class CEventLoop {public:using Clock = std::chrono::steady_clock;using TimePoint = Clock::time_point;using Duration = Clock::duration;using Handler = std::function<void()>;struct TimedHandler {TimePoint time;Handler handler;bool operator<(const TimedHandler& other) const {return time > other.time;}};class IEventLoopHost {public:virtual void onWaitForTask(std::condition_variable& cond, std::unique_lock<std::mutex>& locker) = 0;virtual void onEvent(TimedHandler& event) = 0;virtual void onWaitForRun(std::condition_variable& cond, std::unique_lock<std::mutex>& locker, const TimePoint& timePoint) = 0;};private:IEventLoopHost* host = nullptr;std::priority_queue<TimedHandler> tasks_;std::mutex mutex_;std::condition_variable cond_;std::atomic<bool> running_{ true };public:void setHost(IEventLoopHost* host) {this->host = host;}void post(Handler handler, Duration delay = Duration::zero()) {std::unique_lock<std::mutex> lock(mutex_);tasks_.push({ Clock::now() + delay, std::move(handler) });cond_.notify_one();}void run() {while (running_) {std::unique_lock<std::mutex> lock(mutex_);if (tasks_.empty()) {if (host) {host->onWaitForTask(cond_, lock);}else {cond_.wait(lock, [this] { return !tasks_.empty() || !running_; });}}while (!tasks_.empty() && tasks_.top().time <= Clock::now()) {auto task = tasks_.top();tasks_.pop();lock.unlock();if (host) {host->onEvent(task);}else {task.handler();}lock.lock();}if (!tasks_.empty()) {if (host) {host->onWaitForRun(cond_, lock, tasks_.top().time);}else {cond_.wait_until(lock, tasks_.top().time);}}}}void stop() {running_ = false;cond_.notify_all();}};}// namespace refl// =========================一下为使用示例代码====================================// 用户自定义的结构体
class MyStruct ://public refl::dynamic::IReflectable 	// 如果不需要动态反射,可以不从public refl::dynamic::IReflectable派生public refl::QObject // 这里我们也测试信号槽等功能,因此从这个类派生
{public:~MyStruct() {std::cout << getObjectName() << " destoryed " << std::endl;}int x{ 10 };double y{ 20.5f };int print() const {std::cout << "MyStruct::print called! " << "x: " << x << ", y: " << y << std::endl;return 666;}// 如果需要支持动态调用,参数必须是std::any,并且不能超过4个参数。int print_with_arg(std::any param) const {std::cout << "MyStruct::print called! " << " arg is: " << std::any_cast<int>(param) << std::endl;return 888;}// 定义一个方法,用作槽函数,必须在REFLECTABLE_MENBER_FUNCS列表中,不支持返回值,并且参数必须是std::any,不能超过4个参数。std::any on_x_value_modified(std::any& new_value) {int value = std::any_cast<int>(new_value);std::cout << "MyStruct::on_x_value_modified called! New value is: " << value << std::endl;return 0;}void x_value_modified(std::any param) {REFLEC_IMPL_SIGNAL(param);}REFLECTABLE_PROPERTIES(MyStruct,REFLEC_PROPERTY(x),REFLEC_PROPERTY(y));REFLECTABLE_MENBER_FUNCS(MyStruct,REFLEC_FUNCTION(print),REFLEC_FUNCTION(print_with_arg),REFLEC_FUNCTION(on_x_value_modified),REFLEC_FUNCTION(x_value_modified));DECL_DYNAMIC_REFLECTABLE(MyStruct)//动态反射的支持,如果不需要动态反射,可以去掉这行代码
};//动态反射注册类,注册创建工厂
REGEDIT_DYNAMIC_REFLECTABLE(MyStruct)int main() {// 静态反射部分:std::cout << "---------------------静态反射部分:" << std::endl;auto obj = std::make_shared<MyStruct>();// 打印所有字段名称refl::For<MyStruct>::for_each_propertie_name([](const char* name) {std::cout << "Field name: " << name << std::endl;});// 打印所有字段值refl::For<MyStruct>::for_each_propertie_value(obj.get(), [](const char* name, auto&& value) {std::cout << "Field " << name << " has value: " << value << std::endl;});// 打印所有函数名称refl::For<MyStruct>::for_each_member_func_name([](const char* name) {std::cout << "Member func name: " << name << std::endl;});// 获取特定成员的值,如果找不到成员,则返回默认值auto x_value = refl::get_field_value(obj.get(), "x");std::cout << "Field x has value: " << std::any_cast<int>(x_value) << std::endl;auto y_value = refl::get_field_value(obj.get(), "y");std::cout << "Field y has value: " << std::any_cast<double>(y_value) << std::endl;//修改值:refl::assign_field_value(obj.get(), "y", 33.33f);y_value = refl::get_field_value(obj.get(), "y");std::cout << "Field y has modifyed,new value is: " << std::any_cast<double>(y_value) << std::endl;auto z_value = refl::get_field_value(obj.get(), "z"); // "z" 不存在if (z_value.type().name() == std::string_view("int")) {std::cout << "Field z has value: " << std::any_cast<int>(z_value) << std::endl;}// 通过字符串调用成员函数 'print'auto print_ret = refl::invoke_member_func_safe(obj.get(), "print");std::cout << "print member return: " << std::any_cast<int>(print_ret) << std::endl;std::cout << "---------------------动态反射部分:" << std::endl;// 动态反射部分(动态反射完全不需要知道类型MyStruct的定义):// 动态创建 MyStruct 实例并调用方法auto instance = refl::dynamic::TypeRegistry::instance().create("MyStruct");if (instance) {std::cout << "Dynamic instance type: " << instance->get_type_name() << std::endl;// 这里可以调用 MyStruct 的成员方法auto x_value2 = instance->get_field_value_by_name("x");std::cout << "Field x has value: " << std::any_cast<int>(x_value2) << std::endl;instance->invoke_member_func_by_name("print");instance->invoke_member_func_by_name("print_with_arg", 10);//instance->invoke_member_func_by_name("print_with_arg", 20, 222);//这个调用会失败,命中断言,因为print_with_arg只接受一个函数}// 信号槽部分:std::cout << "---------------------信号槽部分:" << std::endl;auto obj1 = std::make_shared<MyStruct>();obj1->setObjectName("obj1");auto obj2 = std::make_shared<MyStruct>();obj2->setObjectName("obj2");obj2->setParent(obj1);// 连接obj1的信号到obj2的槽函数auto connection_id = obj1->connect("x_value_modified", obj2.get(), "on_x_value_modified");if (!connection_id) {std::cout << "Signal x_value_modified from obj1 connected to on_x_value_modified slot in obj2." << std::endl;}obj1->x_value_modified(42);// 触发信号// 断开连接obj1->disconnect(connection_id);// 再次触发信号,应该没有任何输出,因为已经断开连接obj1->x_value_modified(84);// 使用成员函数指针版本的connectconnection_id = obj1->connect(&MyStruct::x_value_modified, obj2, &MyStruct::on_x_value_modified);if (!connection_id) {std::cout << "Signal connected to slot." << std::endl;}obj1->x_value_modified(666);// 触发信号obj2.reset();obj1.reset();// 事件循环部分:std::cout << "---------------------事件循环部分:" << std::endl;refl::CEventLoop loop;loop.post([]() {std::cout << "Immediate task\n";});//马上执行loop.post([]() {std::cout << "Delayed task\n";}, std::chrono::seconds(1));//延时一秒std::thread loop_thread([&loop]() {loop.run();});std::this_thread::sleep_for(std::chrono::seconds(2));loop.stop();//停止消息循环loop_thread.join();std::cout << "====end=====" << std::endl;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/365880.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GPT-4o文科成绩超一本线,理科为何表现不佳?

目录 01 评测榜单 02 实际效果 什么&#xff1f;许多大模型的文科成绩竟然超过了一本线&#xff0c;还是在竞争激烈的河南省&#xff1f; 没错&#xff0c;最近有一项大模型“高考大摸底”评测引起了广泛关注。 河南高考文科今年的一本线是521分&#xff0c;根据这项评测&…

《塔瑞斯世界》国服震撼登场!AOC助力玩家开启游戏新征途!

一款真正高画质、重机制、轻数值的MMORPG大作&#xff01; 你是否厌倦了在MMORPG游戏中被“氪金大佬”碾压&#xff1f;你是否渴望一个纯粹依靠技术和策略就能获得成就感的游戏世界&#xff1f;如果你对这两个问题的答案都是肯定的&#xff0c;那么《塔瑞斯世界》或许值得你一…

C++ initializer_list类型推导

目录 initializer_list C自动类型推断 auto typeid decltype initializer_list<T> C支持统一初始化{ }&#xff0c;出现了一个新的类型initializer_list<T>&#xff0c;一切类型都可以用列表初始化。提供了一种更加灵活、安全和明确的方式来初始化对象。 class…

2024年道路运输安全员(企业管理人员)备考题库资料。

46.危险货物道路运输随车携带的单据&#xff0c;下列选项不属于的是&#xff08;&#xff09;。 A.道路运输危险货物安全卡 B.运单或者电子运单 C.道路危险货物运输从业资格证 D.车辆检测报告 答案&#xff1a;D 47.危险货物运输驾驶人员在24小时内实际驾驶车辆时间累计不…

老挝-海外媒体发稿:媒体宣发超给力,打造完美产业链

引言 老挝新闻网&#xff08;laosnews&#xff09;通过海外媒体发稿的方式&#xff0c;取得了显著的成就。借助媒体宣发的力量&#xff0c;老挝成功打造了一个完美的产业链&#xff0c;进一步推动了本国经济的发展。本文将探讨老挝-海外媒体发稿的优势以及产业链的构建和发展。…

完美世界|单机版合集(共22个版本)

前言 我是研究单机的老罗&#xff0c;今天给大家带来的是完美世界的单机版合集&#xff0c;一共22个版本。本人亲自测试了一个版本&#xff0c;运行视频如下&#xff1a; 完美世界|单机版合集 先看所有的版本的文件&#xff0c;文件比较大&#xff0c;准备好空间&#xff0c;差…

2024上海CDIE 参展预告 | 一站式云原生数字化平台已成趋势

为什么企业需要进行数字化转型&#xff1f;大家都在讨论的数字化转型面临哪些困境&#xff1f;2024.6.25-26 CDIE数字化创新博览会现场&#xff0c;展位【A18】&#xff0c;期待与您相遇&#xff0c;共同探讨企业如何利用数字化技术驱动业务增长。 一、展会介绍——CDIE数字化…

论坛万能粘贴手(可将任意文件转为文本)

该软件可将任意文件转为文本。 还原为原文件的方法&#xff1a;将得到的文本粘贴到记事本&#xff0c;另存为UUE格式&#xff0c;再用压缩软件如winrar解压即可得到原文件。建议用于小软件。 下载地址&#xff1a;https://download.csdn.net/download/wgxds/89505015 使用演示…

C#中的时间数据格式化详解与应用示例

文章目录 1、基本概念基本格式化方法 2、实用的时间格式化方法格式化日期格式化时间格式化时间戳解析日期时间字符串 3、实际应用4、应用示例结论 在软件开发中&#xff0c;时间数据是无处不在的。无论是用户登录时间、数据备份时间&#xff0c;还是日志记录&#xff0c;都需要…

复制完若依后,idea没有maven窗口

右击项目 添加框架 添加maven框架就可以了

高斯过程的数学理解

目录 一、说明 二、初步&#xff1a;多元高斯分布 三、 线性回归模型与维度的诅咒 四、高斯过程的数学背景 五、高斯过程的应用&#xff1a;高斯过程回归 5.1 如何拟合和推理高斯过程模型 5.2 示例&#xff1a;一维数据的高斯过程模型 5.3 示例&#xff1a;多维数据的高斯过程模…

Kubernetes的发展历程:从Google内部项目到云原生计算的基石

目录 一、起源与背景 1.1 Google的内部项目 1.2 Omega的出现 二、Kubernetes的诞生 2.1 开源的决策 2.2 初期发布 三、Kubernetes的发展历程 3.1 社区的成长 3.2 生态系统的壮大 3.3 重大版本和功能 3.4 多云和混合云的支持 四、Kubernetes的核心概念 4.1 Pod 4.…

Mustango——音乐领域知识生成模型探索

Mustango&#xff1a;利用领域知识的音乐生成模型 论文地址&#xff1a;https://arxiv.org/pdf/2311.08355.pdf 源码地址&#xff1a;https://github.com/amaai-lab/mustango 论文题为**“**利用音乐领域知识开发文本到音乐模型’Mustango’”。它利用音乐领域的知识从文本指…

明日周刊-第14期

不好意思又拖更了哈哈哈。不过赶在7月的第一天&#xff0c;打算更新一下。建党节&#xff0c;值得纪念的一天。 文章目录 一周热点资源分享言论歌曲推荐 一周热点 国内科技新闻 深中通道建成通车 时间&#xff1a;2024年6月30日 内容&#xff1a;深圳至中山跨江通道正式建成开…

【Spring Boot】spring boot环境搭建

1、环境准备 JDK安装&#xff1a;确保安装了Java Development Kit (JDK) 1.8或更高版本。JDK是Java编程的基础&#xff0c;Spring Boot项目需要它来编译和运行。Maven或Gradle安装&#xff1a;选择并安装Maven或Gradle作为项目构建工具。Maven通过pom.xml文件来管理项目的依赖…

PCL 基于点云RGB颜色的区域生长算法

RGB颜色的区域生长算法 一、概述1.1 算法定义1.2 算法特点1.3 算法实现二、代码示例三、运行结果🙋 结果预览 一、概述 1.1 算法定义 点云RGB区域生长算法: 是一个基于RGB颜色信息的区域生长算法,用于点云分割。该算法利用了点云中相邻点之间的颜色相似性来将点云分割成…

nodejs--【Express基本使用】

10 【Express基本使用】 https://www.expressjs.com.cn/ 基于 Node.js 平台&#xff0c;快速、开放、极简的 web 开发框架。 1.Express的安装方式 Express的安装可直接使用npm包管理器上的项目&#xff0c;在安装npm之前可先安装淘宝镜像&#xff1a; npm install -g cnpm -…

【从零开始学架构 架构基础】五 架构设计的复杂度来源:低成本、安全、规模

架构设计的复杂度来源其实就是架构设计要解决的问题&#xff0c;主要有如下几个&#xff1a;高性能、高可用、可扩展、低成本、安全、规模。复杂度的关键&#xff0c;就是新旧技术之间不是完全的替代关系&#xff0c;有交叉&#xff0c;有各自的特点&#xff0c;所以才需要具体…

vue中路由来回切换页面直接卡死

今天发现一个很严重的问题&#xff0c;项目好不容易做好了&#xff0c;结果页面多了&#xff0c;切换之后卡死。页面所有的交互效果都失效了。 排查了许久的错误原因最后发现原来是路由名称重复了。 如上图当页面跳转到riskdetails详细页面之后&#xff0c;框架则被这个详情页…

rga_mm: RGA_MMU unsupported Memory larger than 4G!解决

目录 报错完整log如下:解决方案:报错完整log如下: [ 3668.824164] rga_mm: RGA_MMU unsupported Memory larger than 4G! [ 3668.824305] rga_mm: scheduler core[4] unsupported mm_flag[0x0]! [ 3668.824320] rga_mm: rga_mm_map_buffer map dma_buf err