跨语言接口
全库目录221
本文目录7
C#把中文名字交给原生插件,插件返回一段缓冲。两边即使成功链接,也可能因长度单位、布局或释放方不同而崩溃。跨语言接口先固定线上或ABI上的字节契约,再安排所有权、错误和异步完成,最后才选择绑定工具。
字节表示与长度
设接口接收UTF-8名称。字符串“中A”编码为E4 B8 AD 41,长度是4字节。调用方若把字符数2当容量,接收方会截断中文编码;若接口还要求结尾零,容量需要5字节。契约应分别说明负载长度、容量及零终止是否存在,不能让接收方猜测。
稳定ABI可使用固定位宽整数、字节指针与长度、不透明句柄,以及显式版本和结构大小。C++结构体默认填充、名字修饰与标准库对象布局并非通用跨编译器协议;具体C接口的导出与版本协商见 C++工程边界。进程之间使用序列化值、偏移或稳定ID,普通指针在另一个地址空间没有相同意义。
输出缓冲与所有权
调用方提供缓冲时,可采用“容量不足仅报告所需大小”的协议。一次请求先固定结果版本,查询得到N,再分配N字节并复制同一版本结果。若两次调用之间允许结果改变,第二次仍可能容量不足,因此必须返回新需求、设置重试上限,并让调用者识别版本。
result = Prepare(requestId, inputBytes) → resultHandle
needed = QuerySize(resultHandle)
allocate output[needed]
CopyResult(resultHandle, output, capacity) → writtenBytes, status
ReleaseResult(resultHandle)这是接口机制伪代码,句柄在Prepare成功后由调用者持有,失败时为空。CopyResult在写入前检查容量,成功返回实际字节数;调用者按该长度解码,不读取剩余未初始化容量。结果句柄固定同一次计算,避免把两次不同结果拼成一份数据。
另一种接口由插件分配输出,并导出配套释放函数。分配和释放由同一模块完成,调用者复制需要长期保存的数据后归还。用另一运行库的free或delete释放插件内存,可能跨越不兼容堆。C#封装可把句柄放进具有确定性释放的安全包装,但包装仍须调用正确的原生释放入口。
错误与异步寿命
原生入口在边界捕获内部异常,转换成状态码、操作ID和错误消息。状态区分输入无效、容量不足、暂时不可用与内部失败。空结果可能本来就是合法业务值,因此不能只返回空指针同时表示所有错误。调用方先检查状态,再读取输出,失败不进入后续资产发布。
异步请求还需要独立保存请求ID、输入缓冲与回调所有者。提交返回只表示请求已接纳,回调终态才确认插件停止使用输入。取消后立刻解除固定或释放内存,会让仍在退出的工作线程访问失效地址。
提交 → 插件借用输入与回调
请求取消 → 继续保留借用数据
最终完成 → 确认实际状态与输出
全部回调返回 → 释放输入、回调与结果模块卸载位于最后。先停止新请求,等待在途调用和回调,释放全部结果,再卸载动态库;否则回调函数地址或释放入口可能已消失。线程边界也属于接口:后台只计算纯数据,DCC对象修改回到宿主允许的线程,见 Python宿主集成。
调用粒度与路径
一百万顶点若逐个跨边界,会重复执行类型转换、参数校验与调用跳转。批量接口传连续缓冲、元素数、步长和数据格式,插件校验总字节范围后循环处理。合批减少边界次数,却增加单次缓冲与取消延迟,测量应把序列化、传输和真正算法分别记录。
路径同样需要编码与命名空间。项目相对路径、绝对路径和URI使用不同解析规则;Windows上能找到的大小写错误在区分大小写的平台可能失败。权威大小写来自资产数据库或版本库,规范化不能简单把所有路径转小写。
实现与证据
本文是ABI与进程协议设计,未编译双语言绑定。最小接入应使用“中A”、空值、非法UTF-8、容量少一字节、结果版本变化及取消后迟到回调逐项测试。能链接只验证符号与加载路径,缓冲、寿命与错误分支分别需要运行证据。
相关主题
参考资料
- Microsoft Native interoperability best practices:参数布局、字符串与SafeHandle。
- Python Extending and Embedding:扩展、嵌入与引用所有权。