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 首参;三种模式都可通过 TLScontext_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 里处理它们。