Compare commits
3 Commits
7dfce1484a
...
01815dfde5
| Author | SHA1 | Date | |
|---|---|---|---|
| 01815dfde5 | |||
| e16b4f0a7f | |||
| 4fd72637b8 |
@@ -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
19
src/core/property.cpp
Normal 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
209
src/core/property.h
Normal 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); \
|
||||
} \
|
||||
};
|
||||
@@ -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>;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user