Compare commits

...

3 Commits

6 changed files with 302 additions and 21 deletions

View File

@@ -4,6 +4,7 @@
#include <string_view>
#include "types.h"
#include "property.h"
namespace mirai {
using object_id = u64;
@@ -177,15 +178,25 @@ namespace mirai {
[[nodiscard]] auto weak_from_this_as() const noexcept -> std::weak_ptr<const T> {
return std::weak_ptr<const T>(shared_from_this_as<T>());
}
[[nodiscard]] const std::vector<property_info const*>& get_registered_properties() const noexcept {
return property_infos_;
}
protected:
object();
virtual void on_created() {}
virtual void on_destroying() {}
void register_property(property_info const* prop_info) {
property_infos_.push_back(prop_info);
}
private:
object_id id_;
static std::atomic<object_id> next_id_;
friend class object_registry;
friend property_info;
std::vector<property_info const*> property_infos_;
};
template<typename T>

19
src/core/property.cpp Normal file
View File

@@ -0,0 +1,19 @@
#include "property.h"
#include "object.h"
property_info::property_info(mirai::object* owner_ptr):
name{},
display_name{},
description{},
category{},
type_name{},
flags{property_flags::none},
getter{nullptr},
setter{nullptr},
default_value{},
min_value{std::nullopt},
max_value{std::nullopt},
owner(owner_ptr){
owner->register_property(this);
}

209
src/core/property.h Normal file
View File

@@ -0,0 +1,209 @@
#pragma once
#include <any>
#include <functional>
#include <optional>
#include <string>
#include <string_view>
#include "flag_enum.h"
#include "types.h"
namespace mirai {
class object;
}
enum class property_flags : u32 {
none = 0,
read_only = 1 << 0,
serializable = 1 << 1,
editable = 1 << 2,
hidden = 1 << 3,
notify = 1 << 4,
required = 1 << 5,
deprecated = 1 << 6,
};
MIRAI_FLAG_ENUM(property_flags)
struct property_change_event {
std::string_view property_name;
std::any old_value;
std::any new_value;
void* owner = nullptr;
};
using property_change_listener = std::function<void(const property_change_event&)>;
using property_getter = std::function<std::any(const void*)>;
using property_setter = std::function<bool(void*, const std::any&)>;
struct property_info {
// 属性名称
std::string name;
// 属性显示名称
std::string display_name;
// 属性描述
std::string description;
// 属性类别(用于分组)
std::string category;
// 属性类型名称
std::string type_name;
// 属性标志
property_flags flags{property_flags::none};
// getter 函数(返回 std::any
property_getter getter;
// setter 函数(接受 std::any
property_setter setter;
// 默认值
std::any default_value;
// 最小值(用于数值类型)
std::optional<std::any> min_value;
// 最大值(用于数值类型)
std::optional<std::any> max_value;
mirai::object* owner{nullptr};
property_info(mirai::object* owner_ptr);
// 检查属性是否只读
[[nodiscard]] bool is_read_only() const noexcept {
return has_flag(flags, property_flags::read_only) || !setter;
}
// 检查属性是否可序列化
[[nodiscard]] bool is_serializable() const noexcept {
return has_flag(flags, property_flags::serializable);
}
// 检查属性是否可编辑
[[nodiscard]] bool is_editable() const noexcept {
return has_flag(flags, property_flags::editable) && !is_read_only();
}
// 检查属性是否隐藏
[[nodiscard]] bool is_hidden() const noexcept {
return has_flag(flags, property_flags::hidden);
}
// 检查属性是否启用通知
[[nodiscard]] bool has_notify() const noexcept {
return has_flag(flags, property_flags::notify);
}
// 获取属性值
[[nodiscard]] std::any get_value(const void* obj) const {
if (getter) {
return getter(obj);
}
return {};
}
// 设置属性值
bool set_value(void* obj, const std::any& value) const {
if (is_read_only()) {
return false;
}
if (setter) {
return setter(obj, value);
}
return false;
}
// 获取类型化的属性值
[[nodiscard]] std::optional<std::any> get_value_as(const void* obj) const {
try {
auto value = get_value(obj);
if (value.has_value()) {
return std::any_cast<std::any>(value);
}
}
catch (const std::bad_any_cast&) {
// 类型不匹配
}
return std::nullopt;
}
template <typename T>
bool set_value_as(void* obj, const T& value) const {
return set_value(obj, std::any(value));
}
};
template <typename T>
class property_wrapper {
public:
property_info info_;
// 构造函数:在初始化列表中被调用
property_wrapper(
mirai::object* owner_ptr,
std::string_view name,
std::string_view description,
std::string_view type_name,
property_flags flags,
const T& default_val,
std::function<T(const void*)> internal_getter,
std::function<void(void*, const T&)> internal_setter
) : info_(owner_ptr) {
info_.name = name;
info_.display_name = name; // 默认显示名称等于代码名称
info_.description = description; // 设置注释/描述
info_.type_name = type_name;
info_.flags = flags;
info_.default_value = default_val;
// 适配 lambda 转换为通用的 any 接口
info_.getter = [internal_getter](const void* obj) -> std::any {
return internal_getter(obj);
};
info_.setter = [internal_setter](void* obj, const std::any& val) -> bool {
try {
internal_setter(obj, std::any_cast<T>(val));
return true;
} catch (...) {
return false;
}
};
// 获取基类的指针
}
// 类型化访问
T get(const void* obj) const {
return std::any_cast<T>(info_.get_value(obj));
}
void set(void* obj, const T& val) {
info_.set_value(obj, val);
}
};
#define PROPERTY(Type, Name, Description, Flags, DefaultVal) \
private: \
Type m_##Name = DefaultVal; \
public: \
/* 1. Getter */ \
[[nodiscard]] Type get_##Name() const { return m_##Name; } \
/* 2. Setter */ \
void set_##Name(const Type& value) { \
if (m_##Name != value) { \
m_##Name = value; \
/* 这里可以触发 on_property_changed 通知 */ \
} \
} \
/* 3. 反射包装器 (public 以便外部访问元数据) */ \
property_wrapper<Type> prop_##Name { \
this, \
#Name, \
Description, \
#Type, \
Flags, \
DefaultVal, \
/* Getter Lambda */ \
[](const void* obj) -> Type { \
return static_cast<const std::decay_t<decltype(*this)>*>(obj)->get_##Name(); \
}, \
/* Setter Lambda */ \
[](void* obj, const Type& val) { \
static_cast<std::decay_t<decltype(*this)>*>(obj)->set_##Name(val); \
} \
};

View File

@@ -4,24 +4,22 @@
#include <cwchar>
#include <span>
namespace mirai {
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
using i8 = std::int8_t;
using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using f32 = float;
using f64 = double;
using size_type = std::size_t;
using index_type = std::ptrdiff_t;
using ptrdiff_type = std::ptrdiff_t;
using uintptr_type = std::uintptr_t;
using intptr_type = std::intptr_t;
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
using i8 = std::int8_t;
using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using f32 = float;
using f64 = double;
using size_type = std::size_t;
using index_type = std::ptrdiff_t;
using ptrdiff_type = std::ptrdiff_t;
using uintptr_type = std::uintptr_t;
using intptr_type = std::intptr_t;
using byte_type = std::byte;
using byte_span = std::span<byte_type>;
using const_byte_span = std::span<const byte_type>;
}
using byte_type = std::byte;
using byte_span = std::span<byte_type>;
using const_byte_span = std::span<const byte_type>;

View File

@@ -44,4 +44,47 @@ TEST(test_flag_enum, basic_operations) {
// 检查按位非操作
auto not_flags = ~flags;
EXPECT_EQ(not_flags & my_enum1::cherry, my_enum1::cherry);
// 检查标志存在性
auto has_apple = has_flag(flags, my_enum1::apple);
EXPECT_TRUE(has_apple);
// 检查标志计数
auto count = count_flags(flags);
EXPECT_EQ(count, 1);
// 检查设置和清除标志
set_flag(flags, my_enum1::cherry);
EXPECT_TRUE(has_flag(flags, my_enum1::cherry));
// 清除标志
clear_flag(flags, my_enum1::banana);
EXPECT_FALSE(has_flag(flags, my_enum1::banana));
// 条件设置标志
set_flag_if(flags, my_enum1::banana, false);
EXPECT_FALSE(has_flag(flags, my_enum1::banana));
// 条件清除标志
set_flag_if(flags, my_enum1::banana, true);
EXPECT_TRUE(has_flag(flags, my_enum1::banana));
// 检查是否为空
EXPECT_FALSE(is_empty(flags));
// 清除所有标志
flags = static_cast<my_enum1>(0);
EXPECT_TRUE(is_empty(flags));
// 检查底层值转换
auto underlying = to_underlying(my_enum1::apple | my_enum1::banana);
EXPECT_EQ(underlying, 1);
}
TEST(test_property, basic_operations) {
auto test_obj = mirai::make_obj<test_class1>();
test_obj->set_a(10);
auto a = test_obj->get_a();
EXPECT_EQ(a, 10);
}

View File

@@ -4,6 +4,7 @@
class test_class1 : public mirai::object {
MIRAI_OBJECT_TYPE_INFO(test_class1, mirai::object)
PROPERTY(float, a, "", property_flags::none, 0.0f)
};
class test_class2 : public test_class1 {