C++实现简化版Qt的QObject(3):增加父子关系、属性系统

news2024/12/24 9:13:35

前几天写了文章:
C++实现一个简单的Qt信号槽机制
C++实现简化版Qt信号槽机制(2):增加内存安全保障

之后感觉还不够过瘾,Qt中的QObject体系里还有不少功能特性没有实现。为了提高QObject的还原度,今天我们将父子关系、属性系统等功能也一并实现。

接口设计

首先,我们设计一下我们的接口。
Qt的QObject类过于重,有的时候只用到部分功能的时候没必要引入额外的成员变量。因此我们将父子关系、属性系统这两部分单独抽离成为新的类。
综合考虑下来,我们把支持静态、动态反射,信号槽的类改名为CObject,然后把功能更加完整的类命名为QObject。
在QObject补充父子关系、属性系统这两部分的接口,那么大概是这个情况:

class QObject : public CObject {
public:
	void setObjectName(const char* name);
	
	// 所有权机制&父子关系;
	void setParent(CObject* parent);
	CObject* findChild(const char* name);
	CObject* findChildRecursively(const char* name);

	// 属性功能:
	const std::any &getProperty(const char* name);
	template<typename T>
	const T &getProperty(const char* name);//方便使用的重载
	void setProperty(const char* name, std::any value);
};

我们这里的setParent的所有权强度和Qt的不同,在Qt中parent销毁时一定会直接销毁孩子,即使孩子外部还有强引用,因此Qt的内存管理体系可以分为两套,一套是父子关系,另一个套是QSharedPtr的共享指针。我们为了避免Qt的这种复杂度和不一致性,我们的parent销毁时,只是对孩子的引用计数-1,不一定非要直接销毁孩子。这一点是我们的QObject与Qt的不同点。

成员变量的设计

接口设计完成了,接下来我们开始成员变量的设计。

存储孩子

成员变量的设计受许多因素影响。比较麻烦的是孩子数组的选择和类型的选择,我至少考虑了以下四种的选择情况:
std::list<std::shared_ptr> children;
std::vector<std::shared_ptr> children;
std::list<std::shared_ptrrefl::dynamic::IReflectable> children;
std::vector<std::shared_ptrrefl::dynamic::IReflectable> children;

在以上四种选择情况中,我们预计 QObject 的子对象并不需要随机访问孩子,但是可能会频繁地进行插入和删除操作,因此采用std::list。至于元素类型,我们频繁需要调用refl::dynamic::IReflectable 接口提供的 share_from_this,因此选择 std::shared_ptrrefl::dynamic::IReflectable 会更加适宜,但缺点是需要在一些场景中使用dynamic_cast转换为QObject*。
因此最终选择为std::list<std::shared_ptrrefl::dynamic::IReflectable> children;。

存储属性

这个没太多可说的,直接选择:std::unordered_map<std::string, std::any> properties;

存储父亲

存储父亲指针可以有两个选择:
QObject* parent = nullptr
std::weak_ptrrefl::dynamic::IReflectable parent;
考虑弱引用语义更加准确,并且前面children也是采用IReflectable类型,因此最终选择std::weak_ptrrefl::dynamic::IReflectable parent。

代码实现

由于我们的父亲、孩子都是用了智能指针,因此我们的QObject类在析构的时候,不需要额外操作从父亲移除自己。

getProperty函数直接返回std::any,在使用上稍显繁琐,因此提供指针类型的重载模板函数,提高易用性:

template<typename T>
const T* getProperty(const char* name) {
	try {
		return &std::any_cast<const T&>(properties[name]);
	}
	catch (...) {
		return nullptr;
	}
}

最终,我们的QObject代码如下:

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;
	}

};

内存布局

一个QObject大小为264字节(x64),而一个CObject的大小为104字节。因此,开发者可以根据需要选择从CObject派生或是QObject派生。
在这里插入图片描述
在这里插入图片描述
具体的内存布局如下:
在这里插入图片描述

接下来,最后还需要提供一套异步的事件机制,才算是比较完整地实现了QObject整个体系的功能,敬请期待。

目前完整代码如下:

完整代码

#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>

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) {
						// 如果函数返回空,那么兼容这种case
						std::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) {
			// Detach from the current parent
			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;
		}

	};


}// 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() {
	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);

	// 使用成员函数指针版本的connect
	connection_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);// 触发信号

	obj1.reset();

	std::cout << "====end=====" << std::endl;
	return 0;
}


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

欢迎回家!揭秘“嫦娥六号”背后的守望者

6月25日&#xff0c;嫦娥六号返回器携带来自月背的月球样品安全着陆在内蒙古四子王旗预定区域。这是时隔3年多后&#xff0c;中国探月工程的又一关键节点任务&#xff0c;也是时隔5年多后&#xff0c;嫦娥探测器再去月球背面。 在此次任务中&#xff0c;同元软控数字伴飞团队为…

C++使用Poco库封装一个HTTP客户端类

0x00 前言 我们在使用HTTP协议获取接口数据时&#xff0c;通常需要在Header和Query中添加参数&#xff0c;还有一种就是在Body中追加XML或者JSON格式的数据。本文主要讲述使用Poco库提交HTTP Post请求的Body中附加XML格式的数据&#xff0c;JSON格式的数据类似。 0x01 HttpCl…

Zuul介绍

Zuul 是 Netflix 开源的一个云平台网络层代理&#xff0c;它主要用于路由、负载均衡、中间件通信和动态路由。Zuul 本质上是一个基于 JVM 的网关&#xff0c;它提供了以下功能&#xff1a; 1.路由&#xff1a;Zuul 允许客户端和服务器之间的所有入站和出站请求通过一个中心化的…

Hadoop页面报错Permission denied: user=dr.who, access....

1、临时解决 hdfs dfs -chmod -R 777 /这种方法&#xff0c;存在一个不足&#xff0c;就是后面重新创建的文件夹&#xff0c;页面进行删除的时候&#xff0c;依然报这个错。 但是&#xff0c;对于应付紧急客户需求&#xff0c;可以临时用一下。 2、永久解决 查看页面的Owner…

超声波清洗机怎么选?极力推荐四款口碑大牌超声波清洗机

相信大家都知道超声波清洗机&#xff0c;每次眼镜脏的时候&#xff0c;去眼镜店里让老板帮忙清洗&#xff0c;她们用的就是超声波清洗机&#xff0c;通过超声波的原理深入物品深处清洁&#xff0c;清洁效果非常好。相对手洗的方式&#xff0c;超声波清洗机能够保护镜片在清洗过…

SpringCloud_Ribbon负载均衡

概述 SpringCloud底层其实是利用了一个名为Ribbon的组件&#xff0c;来实现负载均衡功能的。 源码 LoadBalancerInterceptor 其中含有intercept方法&#xff0c;拦截用户的HttpRequest请求&#xff1a; request.getURI() 获取请求uri&#xff0c;即http://userservice/use…

【哈哈大一上学的全忘了,重开!!】STM32从零入门物联网开发

本笔记资料来源 &#xff1a;STM32物联网入门30步&#xff1d;单片机物联网入门教程 WIFI连接阿里云物联网CubeMXHAL库蓝牙ESP8266杜洋主讲_哔哩哔哩_bilibili IOT&#xff1a;Internet of things 学习目标&#xff1a; 1.掌握洋桃IoT开发板的各功能以及驱动与基本应用 2.掌…

Python容器 之 字符串--字符串的常用操作方法

1.字符串查找方法 find() 说明&#xff1a;被查找字符是否存在于当前字符串中。 格式&#xff1a;字符串.find(被查找字符) 结果&#xff1a;如果存在则返回第一次出现 被查找字符位置的下标 如果不存在则返回 -1 需求&#xff1a; 1. 现有字符串数据: 我是中国人 2. 请设计程序…

vue根据文字长短展示跑马灯效果

介绍 为大家介绍一个我编写的vue组件 auto-marquee &#xff0c;他可以根据要展示文本是否超出展示区域&#xff0c;来判断是否使用跑马灯效果&#xff0c;效果图如下所示 假设要展示区域的宽度为500px&#xff0c;当要展示文本的长度小于500px时&#xff0c;只会展示文本&…

mysql5.7.30忘记root密码

windows系统安装了mysql5.7.30&#xff0c;在使用navicat链接mysql时候&#xff0c;提示 如何解决&#xff1a; 打开任务管理器的服务&#xff0c;查看有没有MYSQL服务。 如果没有&#xff0c;则按照下面的csdn博客进行操作。 https://blog.csdn.net/clj198606061111/article…

PostgreSQL的学习心得和知识总结(一百四十七)|深入理解PostgreSQL数据库之transaction chain的使用和实现

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

Firewalld 概述

1.firewalld简介 firewalld的作用是为包过滤机制提供匹配规则(或称为策略),通过各种不同的规则&#xff0c;告诉 netfiter对来自指定源、前往指定目的或具有某些协议特征的数据包采取何种处理方式。 为了更加方便地组织和管理防火墙&#xff0c;firewalld 提供了支持网络区域…

【笔记】手工部署之linux中开放已安装的mysql与tomcat端口

在需要打包的springboot项目中输入mvn clean package 在target下面获得jar包 进入linux中你想要该jar包存在的位置 将jar包上传至linux中 此时在浏览器中输入linux的ip地址&#xff1a;端口号/mapping路径为404 故&#xff1a; 在linux中另开一个标签页 检查mysql和tomcat已…

JMH319【亲测整理】2017剑侠情缘2剑网2汉化版+网游VM单机版+修复纹饰翅膀+内置GM命令无限道具+一键端视频安装教学

资源介绍&#xff1a; 这一套新端早就在手上 一直没分享出来 既然大家放出来了 我也就发出来大家研究吧 目前在改另外一套 是否需要虚拟机&#xff1a;是 文件大小&#xff1a;压缩包约7G 支持系统&#xff1a;win7、win10、win11 硬件需求&#xff1a;运行内存12G …

遇到多语言跨境电商系统源码问题?这里有解决方案!

从手机到电脑&#xff0c;从线下到线上&#xff0c;如今&#xff0c;跨境电商正在打破地域界限&#xff0c;成为全球贸易的新引擎。在这个全球化的背景下&#xff0c;跨境电商平台的运营也面临着一系列的挑战&#xff0c;其中之一就是多语言问题。如果你遇到了多语言跨境电商系…

c进阶篇(三):字符串函数

1.strlen: strlen - C Reference strlen 函数是一个标准库函数&#xff0c;用于计算以 null 结尾的字符串的长度&#xff0c;也就是字符串中实际字符的数量&#xff0c;不包括最后的 null 终止符 \0。它定义在 <string.h> 头文件中。 函数原型:size_t strlen(const ch…

PyCharm 著名编程利器下载安装:Python开发者的必备神器!

PyCharm&#xff0c;它不仅拥有卓越的性能&#xff0c;更以其丰富多样的功能和工具&#xff0c;让开发者在 Python 项目的开发中事半功倍&#xff0c;无论是 Web 开发、科学计算&#xff0c;还是数据分析等领域&#xff0c;都能轻松应对。 在代码编辑方面&#xff0c;PyCharm 的…

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

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

嵌入式学习——硬件(IIC、ADC)——day56

1. IIC 1.1 定义&#xff08;同步串行半双工通信总线&#xff09; IIC&#xff08;Inter-Integrated Circuit&#xff09;又称I2C&#xff0c;是是IICBus简称&#xff0c;所以中文应该叫集成电路总线。是飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备…

Pytorch实战(二)

文章目录 前言一、LeNet5原理1.1LeNet5网络结构1.2LeNet网络参数1.3LeNet5网络总结 二、AlexNext2.1AlexNet网络结构2.2AlexNet网络参数2.3Dropout操作2.4PCA图像增强2.5LRN正则化2.6AlexNet总结 三、LeNet实战3.1LeNet5模型搭建3.2可视化数据3.3加载训练、验证数据集3.4模型训…