使用Boost::Python用C++开发Python库

背景

很多系统都会用json格式进行数据交互。为了保证数据在系统上下游的自动校验,避免数据结构异常带来的系统稳定性问题,可以采用json-schema来定义json接口,并利用json-schema-validator来校验接口响应的结构的合法性。

然而系统中不同子系统的实现(编程语言)并非总是一致,虽然各种语言都提供了json-schema-validator的具体实现,但是不同语言支持的json-schema-validator标准的版本并非完全一致,这会对后续的使用带来一些混乱,例如:

目前大多数语言,例如:Java, Golang, Php, Python, Lua等都支持C/C的扩展。因此,可以用C/C来为其他语言提供统一的扩展库支持,本文就是介绍怎么利用Boost::Python库提供json-schema-validator的Python支持,并介绍如何利用distutils来编译&分发Python库。

Python调用C/C++的方式

有两种方式可以实现Python调用C/C++编写的库:

  • 使用Python扩展
  • 使用ctypes模块直接加载C/C++编写的so

ctypes加载so文件

ctype是Python的外部函数库。它提供了C兼容的数据类型,并允许在DLL或共享库中调用函数。

ctype是Python封装的API函数库。其中cdll = <ctypes.LibraryLoader object>是一个库加载器对象,调用cdll.LoadLibrary便可调用C/C++的so库。

此处不再对如何使用ctype模块加载so文件做过多的介绍,具体可以参考其官方文档

使用Python扩展

该方式是Python为整合其它语言而提供的一种扩展机制,并且该机制不局限于C/C++,也可以用于其它语言。Python的可扩展性具有如下的优点:

  • 方便为语言增加新功能
  • 具有可定制性
  • 可以实现代码复用
  • ……

该方式的具体使用此处也不再做过多介绍,具体信息可以直接参考Python的官方文档Building C and C++ Extensions来了解。接下来要讲的例子就是利用了可以实现代码复用的优点。

Building C and C++ Extensions所示,Python提供了Python/C++ API用来实现Python和C的交互。然而,直接使用这些API来完成Python和C/C的交互还是会存在很多重复工作的。Boost::Python则对Python/C++ API进行了抽象和包装,从而使得Python和C++的交互更加方便。

Boost::Python封装C/C++扩展

接下来我们介绍我们是如何利用Boost::Python来为nlohmann_json_schema_validator增加Python扩展。

nlohmann_json_schema_validator is a C++ library for validating JSON documents based on a JSON Schema which itself should validate with draft-7 of JSON Schema Validation.

编译nlohmann json schema validator库

nlohmann json schema validator支持产出可执行的bin文件以及可供其他项目使用的动态库。为了可以将该扩展包装成Python扩展,我们需要生成该库的动态库。

首先根据nlohmann json schema validator编译文档编译出nlohmann json schema validator的动态库。

Boost::Python封装C/C++代码

在安装Boost的时候,虽然Boost的头文件中存在Boost::Python相关的hpp文件,但是默认却没有该动态库。因此,在使用Boost::Python之前,首先需要安装该库。

1
$ brew install boost-python3

然后,我们用C++编写class json_validator_python;来封装nlohmann json schema validator库,并利用Boost::Python来导出,具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// jsv_python.cpp

#include <iomanip>
#include <iostream>

#include <nlohmann/json-schema.hpp>
#include <boost/python.hpp>

using namespace boost::python;

class JSON_SCHEMA_VALIDATOR_API json_validator_python
{
private:
nlohmann::json_schema::json_validator validator;

public:
json_validator_python() {}
void set_root_schema(const std::string &json_string)
{
validator.set_root_schema(nlohmann::json::parse(json_string.begin(), json_string.end()));
}

void validate(const std::string &json_string) const
{
validator.validate(nlohmann::json::parse(json_string.begin(), json_string.end()));
}
};

// json_schema_validator为module名,必需和生成的so库名一样
BOOST_PYTHON_MODULE(json_schema_validator)
{
class_<json_validator_python, boost::noncopyable> ("json_validator_python")
.def("set_root_schema", &json_validator_python::set_root_schema)
.def("validate", &json_validator_python::validate)
;
}

如上所示的BOOST_PYTHON_MODULE代码,目的是导出类及成员方法,这些导出的类和方法可以在Python中调用。

关于Boost::Python更详细的使用,可以参考其官方文档

编译并产出Python扩展

1
2
3
4
5
6
7
8
9
10
11
12
13
g++ --std=c++11 -fPIC -c jsv_python.cpp \
-I${json-schema-validator_PATH}/src \
-I${nlohmann-json_PATH} \
-I${Boost_PATH}/include \
-I${Python_INCLUDE_PATH}

g++ --std=c++11 -shared \
-L${json-schema-validator_LIB_PATH} \
-L${Python_LIB_PATH} \
-L${Boost_Python_LIB_PATH} \
-lnlohmann_json_schema_validator \
-lboost_python38 -lpython3.8 \
-o json_schema_validator.so jsv_python.o

如上的指令,会生成封装之后的Python扩展:json_schema_validator.so

测试python扩展

在当前目录执行如下的Python代码,可以发现,我们封装的扩展已经可以当做Python扩展来导入并使用了。

1
2
3
4
5
6
7
8
9
10
11
12
13
import json_schema_validator as jsv

validator = jsv.json_validator_python()


isValidator = True
try:
validator.set_root_schema('{"type":"object", "properties": {"a":{"type": "string"}}}')
validator.validate('{"a":"1"}');
except:
isValidator = False

print(isValidator)

使用distutils编译并分发扩展

为了使用方便,使用Building C and C++ Extensions介绍的distutils模块编译Python扩展。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from distutils.core import setup, Extension
import os

os.environ["CC"] = "g++"
os.environ["CXX"] = "g++"

module1 = Extension('json_schema_validator',
include_dirs = ['../../src',
'../../',
'/usr/local/Cellar/boost/1.72.0_3/include',
'/usr/local/opt/python@3.8/Frameworks/Python.framework/Versions/3.8/include/python3.8'],
libraries = ['boost_python38', 'python3.8',
'nlohmann_json_schema_validator'],
library_dirs = ['/usr/local/opt/python@3.8/Frameworks/Python.framework/Versions/3.8/lib',
'/usr/local/Cellar/boost-python3/1.72.0_1/lib',
'.'],
sources = ['jsv_python.cpp'],
extra_compile_args=['--std=c++11'],
extra_link_args=['--std=c++11'])

setup (name = 'json-schema-validator',
version = '1.0',
description = 'json schema validator',
ext_modules = [module1])
1
2
3
$ python3 setup.py build
$ python3 setup.py install
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib/python3.7/site-packages/json_schema_validator

然后就可以在任意位置,执行测试python扩展一节的测试代码啦。

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2020-2024 Wang Wei
  • 本站访问人数: | 本站浏览次数:

请我喝杯咖啡吧~

微信