前言
在构建大型分布式系统时,为了保证数据在系统上下游的自动校验,避免数据结构异常带来的系统稳定性问题,通常会采用 JSON 格式进行数据交互。此时,可以采用 JSON Schema 来定义接口规范,并利用 JSON Schema Validator 来校验接口响应结构的合法性。
然而,系统中不同子系统的实现语言往往并非一致。虽然主流编程语言都提供了 JSON Schema Validator 的具体实现,但不同语言支持的 JSON Schema 标准版本(Draft)并不完全统一,这会给后续的系统集成带来混乱。例如:
- Java: json-schema-validator 支持 Draft 4
- C++: json-schema-validator 支持 Draft 7
- Python: jsonschema 支持 Draft 7
目前大多数语言(如 Java, Golang, PHP, Lua 等)都支持 C/C++ 的扩展机制。因此,最佳实践是用 C/C++ 编写核心逻辑库,再为其他语言提供统一的扩展包。本文详细介绍如何利用 Boost::Python 库将 C++ 的 nlohmann_json_schema_validator 封装为 Python 扩展,并介绍如何使用 distutils 进行编译与分发。
Python 调用 C/C++ 的方式
主要有两种方式可以实现 Python 调用 C/C++ 编写的库:
- ctypes:Python 内置的外部函数库,提供 C 兼容的数据类型,允许在 DLL 或共享库中调用函数。
- Python Extension API:Python 官方提供的扩展机制,通过编写 C/C++ 代码生成
.so(Linux/Mac) 或.pyd(Windows) 文件,被 Python 解释器直接加载。
ctypes 加载 so 文件
ctypes 是 Python 封装的 API 函数库。其中 cdll = <ctypes.LibraryLoader object> 是一个库加载器对象,调用 cdll.LoadLibrary 便可调用 C/C++ 的 so 库。
import ctypes
lib = ctypes.CDLL('./libmylib.so')
lib.my_function.argtypes = [ctypes.c_int]
lib.my_function.restype = ctypes.c_int
result = lib.my_function(10)
此处不再对如何使用 ctypes 模块加载 so 文件做过多的介绍,具体可以参考其官方文档。对于复杂的类封装和内存管理,直接使用 ctypes 较为繁琐且容易出错。
使用 Python 扩展
该方式是 Python 为整合其它语言而提供的一种扩展机制。Python 的可扩展性具有如下优点:
- 方便为语言增加新功能
- 具有高度可定制性
- 可以实现底层代码复用
- 性能更优(相比 ctypes 的边界转换开销)
该方式的具体使用可以参考 Python 官方文档 Building C and C++ Extensions。接下来要讲的例子就是利用了'代码复用'的优点。
Python 提供了 Python/C++ API 用来实现 Python 和 C++ 的交互。然而,直接使用这些 API 来完成交互会存在很多重复工作,例如手动处理引用计数、类型转换等。Boost::Python 则对 Python/C++ API 进行了抽象和包装,使得 Python 和 C++ 的交互更加方便,类似于 pybind11 但基于 Boost 生态。
Boost::Python 封装 C/C++ 扩展
接下来我们介绍如何利用 Boost::Python 为 nlohmann_json_schema_validator 增加 Python 支持。
nlohmann_json_schema_validator 是一个基于 JSON Schema 验证 JSON 文档的 C++ 库,它本身应验证符合 JSON Schema Validation draft-7 标准。


