runtime_object 运行时动态对象

头文件:<mc/engine/runtime_object.h>

概述

在运行期(而非编译期反射)定义 interface 与 object 的动态对象机制,是第三方动态宿主或语言绑定层把自身类型接入 mcengine 对象模型的底座。

主要特性

  • 运行期元数据:runtime_interface_metadata / runtime_object_metadata 在运行期增量定义属性与方法,自动注册进 reflection_factory(RAII 句柄,元数据析构即注销)
  • 完整对象语义:runtime_object 继承 object_impl,属性读写、from/to_variant 序列化、信号、标准接口(Properties/Introspect 等)全部复用 engine 通用路径
  • 方法派发单轨:方法调用与 C++ 静态 mc::engine::object 同走 object_impl 反射通道;接口方法的 runtime_method_info::do_async_invoke 自转发到所属接口的类型级 dispatcher(runtime_interface_metadata::set_method_dispatcher)。宿主语言(如 Lua)经此把调用路由到自身实现,实例 self 由 runtime_object::set_impl_host 提供,不再有 per-instance 方法/属性 handler
  • 参数契约明确:runtime method 可标记为 variadic、declared 或 explicit_context。前者保留原始参数,declared 方法只接收签名声明的业务参数,explicit_context 方法保留显式 context 首参;三种模式都可通过 TLS context_stack 获取当前 context
  • 统一工厂接入:get_or_create 注册的 class 自动接入 abstract_object_factory,可被按 class_name 反查实例化;没有类型级 creator 时直接创建普通 runtime_object
  • 类型实现 creator:每个 runtime_object_metadata 可绑定一个可撤销的 implementation creator;绑定存在时优先创建宿主语言完整实现,撤销后恢复通用 runtime object
  • 属性别名:对象层属性别名表消解多接口同名属性的去重冲突
  • 元数据版本:revision 跟踪变更,对象元数据惰性重建
  • 共享属性索引:属性名与槽位映射保存在类型元数据中,实例仅为实际访问的属性按需创建稠密槽,避免重复的哈希节点、bucket 和属性名存储

基本用法

示例 1:定义动态接口与对象,读写属性

#include <mc/engine/runtime_object.h>

auto scanner = mc::make_shared<mc::engine::runtime_interface_metadata>("test.runtime.Scanner");
// 属性配置全部落在 runtime_property_info 上;of<T> 按 C++ 类型推导 D-Bus 签名
scanner->add_property(mc::engine::runtime_property_info::of<bool>("Enabled", true));
scanner->add_property(mc::engine::runtime_property_info::of<mc::string>("Label", "default"));
// 需要显式签名/flags 时直接构造:签名 s、只读
scanner->add_property(std::make_unique<mc::engine::runtime_property_info>(
    "Serial", "s", mc::variant(mc::string("N/A")), MC_REFLECT_FLAG_READONLY));

auto metadata = mc::make_shared<mc::engine::runtime_object_metadata>("test.runtime.Object", "/test/runtime/Object");
metadata->add_interface(scanner);

mc::engine::runtime_object object(metadata);
object.get_property("Enabled", {}, 0);                       // true(宽松:未知属性返回空 variant)
object.set_property("Label", mc::variant("hello"));
object.get_property_checked("Label");                        // 严格:未知属性抛异常
object.set_property_checked("Enabled", false, "test.runtime.Scanner");

示例 2:动态方法经类型级 dispatcher 转发

auto iface = mc::make_shared<mc::engine::runtime_interface_metadata>("test.runtime.HandlerIface");
iface->add_method(mc::engine::runtime_method_info::of("Ping"));               // 签名可留空,后续可升级
iface->add_method(mc::engine::runtime_method_info::of("Ping", "a{ss}s", "s")); // 同名方法用完整 schema 升级

// 方法实现宿主在类型(interface metadata)上:dispatcher 无状态,多实例共享;实例 self 经
// iface.get_owner()->get_impl_host() 取回(此处宿主存的是宿主语言对实例的引用持有器)。
iface->set_method_dispatcher([](mc::engine::runtime_interface& itf, mc::string_view method,
                                const mc::variants& args) -> mc::reflect::async_result {
    // 转发给宿主语言实现;普通值包装为已就绪 result,异步则透传 future
    return mc::result<mc::variant>(mc::variant(mc::string("pong")));
});

auto metadata = mc::make_shared<mc::engine::runtime_object_metadata>("test.runtime.Obj", "/test/runtime/Obj");
metadata->add_interface(iface);

mc::engine::runtime_object object(metadata);
object.set_impl_host(my_instance_holder);                    // per-instance self(宿主语言注入)
object.invoke("Ping", {}, "test.runtime.HandlerIface");      // "pong"(invoke 内部走 async_invoke().get())

动态宿主若声明了业务参数签名,应使用 declared 模式;框架在 RPC 转发时会消费自身携带的 wire context,宿主实现不会多收到一个参数。无签名方法使用 variadic,不会根据首个参数类型猜测并删除业务参数。只有确实把 wire context 作为 handler 首参的实现才使用 explicit_context:

using mode = mc::engine::runtime_method_parameter_mode;
iface->add_method(mc::engine::runtime_method_info::of("Add", "ii", "i", mode::declared));
iface->add_method(mc::engine::runtime_method_info::of("WithContext", "i", "i", mode::explicit_context));
iface->add_method(mc::engine::runtime_method_info::of("Raw", "", "v", mode::variadic));

示例 3:经自发现工厂按 class_name 实例化

// 必须走 get_or_create,工厂回调用 try_get(class_name) 反查 metadata
auto meta = mc::engine::runtime_object_metadata::get_or_create("test.runtime.Cls", "/test/runtime/Cls");
auto iface = mc::make_shared<mc::engine::runtime_interface_metadata>("test.runtime.ClsIface");
iface->add_property(mc::engine::runtime_property_info::of<bool>("Enabled", true));
meta->add_interface(iface);

auto obj = mc::engine::try_create_abstract_object("test.runtime.Cls");  // 非空
// 注意:裸 make_shared 构造的 metadata 不进 registry,try_create 返回空

示例 4:为单个 runtime 类型绑定实现 creator

auto meta = mc::engine::runtime_object_metadata::get_or_create(
    "test.runtime.Custom", "/test/runtime/Custom");

auto registration = meta->register_implementation_creator(
    [](mc::shared_ptr<mc::engine::runtime_object_metadata> metadata)
        -> mc::shared_ptr<mc::engine::abstract_object> {
        return mc::make_shared<mc::engine::runtime_object>(std::move(metadata));
    });

if (!registration) {
    throw std::runtime_error("该 runtime 类型已经绑定实现");
}

auto custom = mc::engine::try_create_abstract_object("test.runtime.Custom");
registration.reset(); // 后续创建恢复使用通用 creator

API 详细说明

runtime_property_info

运行期 property 的结构化描述对象(继承 property_type_info),自包含 name/signature/default_value/flags。 外部构建好后经 runtime_interface_metadata::add_property 收集——配置统一落在本对象上,interface 仅作收集器。 typeinfo() 恒为 mc::variant、type_name() 恒 "runtime"。

方法 参数 返回值 异常 线程安全 说明
runtime_property_info(name, signature, default={}, flags=0) mc::string, mc::string, mc::variant, std::uint32_t - 分配失败抛 std::bad_alloc - 显式构造;数组签名(a 非 a{)的空默认归一为空 array;flags 含 MC_REFLECT_FLAG_*(readonly/explicit/no_emit/invalidated/nocache)
of<T>(name, default, flags=0)(静态模板) mc::string_view, T, std::uint32_t=0 std::unique_ptr<runtime_property_info> 分配失败抛 std::bad_alloc - 便捷工厂:按 C++ 类型 T 推导 D-Bus 签名(b/s/y/q/u/i/x/t/d/v/a{sv}…)
default_value() - const mc::variant& 不抛出 是 类型默认值(供 make_property 实例化种子)
is_read_only() - bool 不抛出 是 flags 是否含 MC_REFLECT_FLAG_READONLY
get_signature() - mc::string_view 不抛出 是 D-Bus 签名
clone() - member_info_base* 分配失败抛 std::bad_alloc 是 深拷贝(含默认值与 flags),rebuild/拷贝时使用

runtime_method_info / runtime_signal_info

运行期 method / signal 的结构化描述(分别继承 method_type_info / signal_info_base),自包含 name 与 实参/返回签名,外部构建好后经 add_method / add_signal 收集。

runtime_method_parameter_mode 的取值决定宿主 handler 的参数形状:variadic 保留原始参数,declared 按业务签名接收参数并由框架消费 wire context,explicit_context 按业务签名登记但将 wire context 作为 handler 首参保留。TLS context_stack 对三种模式都可用。

参数模式由 method 自身私有存储保存,继承的 data、flags 仍供宿主自定义使用;注册前设置这些扩展值不会改变参数模式或公开签名,复制及 clone() 同样保留两者。

方法 参数 返回值 异常 线程安全 说明
runtime_method_info(name, args_sig, result_sig) mc::string×3 - 分配失败抛 std::bad_alloc - 非空参数签名默认为 declared,空签名表示 variadic
runtime_method_info(name, args_sig, result_sig, parameter_mode) mc::string×3 + runtime_method_parameter_mode - 分配失败抛 std::bad_alloc,非法模式抛 mc::invalid_argument_exception - 显式选择 variadic、declared 或 explicit_context;显式 context 模式自动在完整绑定签名前追加 a{sv}
runtime_method_info::of(...)(静态) mc::string_view×1~3,或加 runtime_method_parameter_mode std::unique_ptr<runtime_method_info> 同上 - 便捷工厂;Lua mc.engine.register 对提供 args 的方法使用 declared,省略 args 使用 variadic
runtime_signal_info(name, args_sig, result_sig) mc::string×3 - 同上 - 显式构造
runtime_signal_info::of(name, args_sig={}, result_sig={})(静态) mc::string_view×3 std::unique_ptr<runtime_signal_info> 同上 - 便捷工厂

runtime_interface_metadata

运行期 interface 元数据,进程级按名注册,析构自动从 reflection_factory 注销。

方法 参数 返回值 异常 线程安全 说明
runtime_interface_metadata(name) mc::string - 分配失败抛 std::bad_alloc - 构造并注册进 reflection_factory(RAII 句柄持有)
get_name() - mc::string_view 不抛出 是 接口名
get_revision() - std::uint64_t 不抛出 是 元数据版本号,每次变更递增
get_metadata() - const metadata_list& 不抛出 否 engine 元数据视图
add_property(info) std::unique_ptr<runtime_property_info> void 分配失败抛 std::bad_alloc 否 收集一个外部构建好的 property 描述;info 自包含 name/signature/default/flags。info 为空或重名则忽略(幂等)
add_method(info) std::unique_ptr<runtime_method_info> void 同上 否 收集 method 描述;空签名的 variadic 描述可被后续完整签名升级一次,已有完整签名则忽略;info 为空忽略。便捷构造见 runtime_method_info::of
add_signal(info) std::unique_ptr<runtime_signal_info> void 同上 否 收集 signal 描述;重名或空 info 忽略。便捷构造见 runtime_signal_info::of
set_method_dispatcher(dispatcher) runtime_method_dispatcher void 不抛出 否 注入类型级方法派发宿主(如 Lua 类表);runtime_method_info::do_async_invoke 自转发到此回调,self 经 runtime_interface::get_owner 取得。空表示该接口无外部宿主,方法走 object_impl 反射(runtime 方法无 C++ 实现即抛 not_implemented)
get_method_dispatcher() - const runtime_method_dispatcher& 不抛出 是 取类型级方法派发宿主
make_property(name) mc::string_view std::unique_ptr<property_helper> 不抛出 否 按 info 的签名/默认值构造属性存储实例
find_property(name) mc::string_view const runtime_property_info* 不抛出 否 查询已登记属性描述,未找到返回 nullptr
find_method(name) / find_signal(name) mc::string_view const runtime_method_info* / const runtime_signal_info* 不抛出 否 查询方法/信号描述,未找到返回 nullptr
get_or_create(name)(静态) mc::string_view mc::shared_ptr<runtime_interface_metadata> 分配失败抛 std::bad_alloc 是 进程级 registry 按名复用或创建
try_get(name)(静态) mc::string_view mc::shared_ptr<runtime_interface_metadata> 不抛出 是 只查不创建,未注册返回空

runtime_object_metadata

运行期对象元数据:class_name + path_pattern + interface 列表 + 成员别名表。

方法 参数 返回值 异常 线程安全 说明
runtime_object_metadata(class_name, path_pattern) mc::string, mc::string - 分配失败抛 std::bad_alloc - 构造并注册进 reflection_factory 与 abstract_object_factory
add_object_property(info) std::unique_ptr<runtime_property_info> void 分配失败抛 std::bad_alloc 否 收集对象级私有 property:进入 object metadata 顶层成员,实例存储挂到 object_private_interface,不作为对外 interface 暴露;info 自包含 name/signature/default/flags
add_interface(iface_metadata) mc::shared_ptr<runtime_interface_metadata> void 同上 否 追加 interface,revision 递增
set_member_aliases(aliases) member_alias_map void 同上 否 对象层成员别名表(interface → 原名 → 别名),对 property/method/signal 一视同仁,消解多接口同名成员冲突
get_class_name() / get_path_pattern() - mc::string_view 不抛出 是 基本信息
get_revision() - std::uint64_t 不抛出 是 含各 interface revision 的聚合版本
get_metadata() - const object_metadata& 分配失败抛 std::bad_alloc 否 惰性重建的对象元数据
find_object_property(name) mc::string_view const runtime_property_info* 不抛出 否 查询对象级私有 property 描述,未找到返回 nullptr
get_interfaces() - interface 列表引用 不抛出 否 已添加的 interface 元数据
register_implementation_creator(creator) runtime_object_factory_fn runtime_object_creator_registration 分配失败抛 std::bad_alloc 是 为该类型绑定唯一实现 creator;已绑定或 creator 为空时返回空句柄
try_create_implementation() - optional<shared_ptr<abstract_object>> creator 异常原样传播 是 有绑定时调用并返回其结果(结果可为空);未绑定返回 nullopt,仅此时统一工厂才回退创建普通 runtime_object
get_or_create(class_name, path_pattern)(静态) mc::string_view, mc::string_view mc::shared_ptr<runtime_object_metadata> 分配失败抛 std::bad_alloc 是 进程级 registry 复用或创建;自发现实例化必须走此入口
try_get(class_name)(静态) mc::string_view mc::shared_ptr<runtime_object_metadata> 不抛出 是 只查不创建

runtime_object_creator_registration

move-only RAII 句柄,持有期间对应 metadata 使用已绑定的 implementation creator。

方法 参数 返回值 异常 线程安全 说明
move 构造 / move 赋值 同类型右值 - 不抛出 否 转移 creator 撤销责任
operator bool() - bool 不抛出 否 是否为有效注册
reset() - void 不抛出 不同句柄可并发;同一句柄否 提前撤销;重复调用安全,metadata 恢复通用 creator

runtime_object

继承 object_impl 的动态对象,接口实例为独立堆对象(经虚函数 get_interface_from_info 解析)。属性实例按类型元数据槽位惰性创建;接口按元数据顺序解析,不为每个对象重复维护名称哈希表。

方法 参数 返回值 异常 线程安全 说明
runtime_object(metadata, parent) mc::shared_ptr<runtime_object_metadata>, core_object*=nullptr - 分配失败抛 std::bad_alloc - 按元数据构造接口实例
get_class_name() / get_path_pattern() / get_metadata() - 对应视图 不抛出 是 覆写自 abstract_object
get_runtime_interface(name) mc::string_view runtime_interface* 不抛出 否 动态接口实例,未找到返回 nullptr
get_property_checked(name, iface) mc::string_view, std::optional<mc::string_view>={} mc::variant 属性不存在抛 mc::bad_property_exception 否 严格读:iface 省略时按对象元数据首属性规则定位
set_property_checked(name, value, iface) mc::string_view, const mc::variant&, std::optional<mc::string_view>={} void 属性不存在抛 mc::bad_property_exception 否 严格写
set_impl_host(host) std::shared_ptr<void> void 不抛出 否 不透明实例实现宿主(宿主语言存对 Lua 实例引用的持有器);类型级 dispatcher 经 get_owner()->get_impl_host() 取回 self
get_impl_host() - const std::shared_ptr<void>& 不抛出 是 取实例实现宿主句柄

方法 invoke/async_invoke/has_method/set_property 不再覆写,全部复用 object_impl:接口方法经 runtime_method_info::do_async_invoke 自转发到接口的类型级 dispatcher,与 C++ 静态 mc::engine::object 同一派发单轨。

最佳实践

✅ 自发现场景统一走 get_or_create

auto meta = mc::engine::runtime_object_metadata::get_or_create("my.Cls", "/my/cls");
// 工厂回调 try_get 能反查到 registry 里的 metadata,按 class_name 实例化成功
auto obj = mc::engine::try_create_abstract_object("my.Cls");

✅ 方法先占位、后升级签名

iface->add_method(mc::engine::runtime_method_info::of("GetData"));                    // 先占位保证方法可见
iface->add_method(mc::engine::runtime_method_info::of("GetData", "a{ss}as", "a(sas)")); // 拿到 schema 后升级,不会降级

✅ 对象级私有属性放在 object metadata

auto meta = mc::engine::runtime_object_metadata::get_or_create("my.Cls", "/my/cls");
meta->add_object_property(mc::engine::runtime_property_info::of<std::int32_t>("_cache", 0));
// _cache 可经 object_impl 属性路径读写,但由 object_private_interface 承载通知路由,不暴露为对外 interface。

❌ 裸 make_shared 构造 metadata 又期望自发现实例化

auto meta = mc::make_shared<mc::engine::runtime_object_metadata>("my.Cls", "/my/cls");
// 未进 registry,try_get 取空:
mc::engine::try_create_abstract_object("my.Cls"); // 返回空!

✅ 方法宿主在类型、self 在实例

// dispatcher 无状态、挂在 interface metadata 上(多实例共享),实例 self 经 impl_host 配对:
iface->set_method_dispatcher([](mc::engine::runtime_interface& itf, mc::string_view m,
                                const mc::variants& args) -> mc::reflect::async_result {
    auto* owner = static_cast<mc::engine::runtime_object*>(itf.get_owner());
    auto  self  = owner->get_impl_host();      // 取回宿主语言注入的实例引用
    /* ...路由到 self 的实现... */
    return mc::result<mc::variant>{};
});

❌ 期望 dispatcher 收到 Object.Properties 的 *WithContext 方法

// Get/Set/GetAllWithContext 是 common_properties 接口的真实 C++ 方法,由 object_impl 原生派发,
// 不会经 runtime_method_info 进入类型级 dispatcher——不要依赖在 dispatcher 里处理它们。