工程脚本插件方案 - c集成Python基础篇
工程脚本插件方案 - c集成Python基础篇
在做比较大型的工程时,1般都会分核心层和业务层。核心层要求实现高效和稳定的基础功能,并提供调用接口供业务层调用的1种标准的框架划分。在实际中根据需求会拆分的更细。 外部的表现情势就是1个核心动态库,带着1堆业务业务动态库。通过1个调度程序把这些链接起来,外加1堆配置文件,就构成1个完成的项目。 这类模式在1个团队开发中,工作职责比较容易划分。制定API接口后,开发工作基本可以并行实现,包括后期的功能测试(白盒、黑盒)。不管工程使用甚么语言,基本都是如此。 c语言无疑是很强大而又灵活的,但是开发比较复杂,开发工期比较长。全部使用c/c++ 进行开发的话,编译调试整合发布,就需要大量时间,包括运营保护的话,呵呵~ 。全部产品的生命周期需要投入大量的人力来保持这个产品。特别是对做定制的终究用户的话,那便可能就是1场旷日持久战。 刚才说的1般工程都会包括2方面,核心和扩大。是公司级产品的话,1定会出于盈利问题,会对核心做保密(商业机密)。扩大是在核心基础上实现的,很大程度上保密等级就没那末高,乃至可以是开放式的。方便有1定能力的用户直接扩大。这是最理想的1种情况。 如果业务层还是使用c/c++的话,估计用户没有几个有能力或说不太愿意去做扩大。只能公司团队进行保护和扩大。固然开源项目除外,活跃的开源项目还是有很多大侠们愿意去扩大的。当下降扩大的难度,不但可以缩短开发周期下降本钱,下降运营保护本钱。如果产品够优秀,还能吸引有1定能力的客户来帮忙做扩大。扩大简单方便,用户自己都能扩大,项目运营本钱一定下降。这就皆大欢乐。 脚本是1个非常方便的东西,不需要编译直接运行就可以看到结果。不用斟酌大量系统相干的开发技能,更贴近实际业务的描写,修正问题不需要重新编译发布,保护非常方便。能大大提供生产效力,这就是我们所须要的。现在流行的脚本语言有很多Ruby、Perl、Python、Lua、Javascript等等。。。,反正很多。
使用那种脚本作为扩大,要看实际项目和现有资源的情况而定。本文只介绍python的集成。 空话这么多,主要的目的就下面几个。
集成: 就是通过简单、直接和快速的在不同语言直接调度切换控制,属于无缝连接。就像使用同1种语言在不同的动态库之间调度。而不是那种使用套接字和管道等的间接调度。 混合开发中,不管python或C都可以作为“上层”。因此两方面都要提供入口提供对方调度。这个其实和正常的同1语言编写的插件模式是1致的。核心提供接口和注册入口,扩大注册入口并调用接口。
1、准备环境使用的Python是3.5版本的。 首先肯定是须要个python的运行环境,可以直接从官网python.org下载Python3.x 进行安装。建议直接下源代码编译,由于里面有很多代码可以参考。 windows配置python安装路径以下为例 1、 增加python搜索路径,方便代码运行调试 path=d:python;d:pythonpcbuildwin32 2、 增加python环境路径。加载模块时默许会从配置路径中搜索。 PYTHONPATH=.;d:pythonlib;d:pythonpcbuildwin32;D:PythonLibsite-packages;d:python 3、 增加编译路径。 PyInc=d:pythoninclude;d:pythonpc
PyLib=d:pythonpcbuildwin32 方便VS搜索路径配置。在工程中,只需援用配置变量路径,可以直接使用
2.1、嵌入Python第1个简单工程创建测试工程在VS中创建1个空的VC++控制台程序。在工程选择的搜索目录中加入 选择菜单:Project->Properties... 在 使用环境变量的1个好处是,方便不同机器不同环境的切换,不需要修改工程配置。
创建script.py脚本在工程中创建1个新文件,直接改名为
"""
在C中调用Python模块运行。
调用时须要把文件放在程序当前运行目录(保证在搜索目录中)。
www.moguf.com 2016-05⑵8
"""
message = 'hello life...'
def transform(input):
input = input.replace('life','Python')
return input.upper() 在Python环境中运行这个脚本,可以得到下面结果。可以看到打印出 Microsoft Windows [版本 6.1.7601]
版权所有 (c) 2009 Microsoft Corporation。保存所有权利。
C:UsersCrystalIce>cd /d D:DevMySimplepythonincembedsimple
D:DevMySimplepythonincembedsimple>python
Running Release|Win32 interpreter...
Python 3.5.0 (default,Nov 4 2015,21:58:28) [MSC v.1900 32 bit (Intel)] on win32
Type "help","copyright","credits" or "license" for more information.
>>> import script
>>> print(script.message)
hello life...
>>> x = script.message
>>> print(script.transform(x))
HELLO PYTHON...
>>> 创建hello.c在工程中添加1个新文件,命名为 //
// C code runs Python code in this module in embedded mode.
// print hello string
//
// www.moguf.com 2016-05⑵8
//
#include <python.h>
int main()
{
Py_Initialize();
PyRun_SimpleString("print('run python ...')");
PyRun_SimpleString("import script");
PyRun_SimpleString("print(script.message)");
PyRun_SimpleString("x = script.message");
PyRun_SimpleString("print(script.transform(x))");
Py_Finalize();
} 运行程序在程序最后下个断点,方便查看运行结果。直接运行程序可以看到下面结果。 run python ...
hello life...
HELLO PYTHON...
基本调用流程解析使用C程序运行Python脚本代码,可以通过使用Python字符串,调用Python对象或模板之类的所有操作。
上面代码嵌入进程很容易。但在实际使用中想要更好的整合,须要了解提供的API和不同语言之间的转换。 Python嵌入C的基础API下面几个基础API,在C中能很容易的履行Python脚本中的代码。包括字典、数组和对象。固然想要更好的混合交互须要熟习所有的API。
2.2、使用C扩大Python2.1的内容只是通过C程序调用Python脚本,要让Python脚本能调用C代码,就须要扩大。用C扩大Python功能那就简单很多。有很多实例可以参考。Python源代码就是宝库。 创建扩大工程 hello在VS中创建1个空的动态库 在VS生成时有些特殊。生成的后缀选择.pyd主要是为避免和系统.dll产生冲突。 在工程选项界面中设置工程输出名称为 并在Linker页面Input组中设置库依赖为
创建hello.c扩大代码在工程中新建hello.c文件,复制下面内容。 //
// A simple C extension module for python,called "hello"
//
// www.moguf.com 2016-05⑵8
//
#include <python.h>
#include <string.h>
//
// module functions
//
static PyObject * // returns object
message(PyObject *self,PyObject *args)
{
char *fromPython,result[1024];
if (!PyArg_Parse(args,"(s)",&fromPython)) // convert Python -> C
return NULL; // exception null = raise
else {
strcpy(result,"Hello,"); // build up C string
strcat(result,fromPython); // add passed Python string
return Py_BuildValue("s",result); // convert C -> Python
}
}
//
// registration methods table
static PyMethodDef hello_methods[] = {
{ "message",message,METH_VARARGS,"func doc" },// format: name,&func,fmt,doc
{ NULL,NULL,0,NULL } // end
};
// module definition structure
static struct PyModuleDef hellomodule = {
PyModuleDef_HEAD_INIT,"hello",// module name
"mod doc",// module documentation,
-1,hello_methods // methods table
};
//
// module initializer
PyMODINIT_FUNC
PyInit_hello()
{
return PyModule_Create(&hellomodule);
} 上面的代码主要分为4块。
通过上述定义为Python脚本调用提供访问入口,这就是通常所说的胶水代码。具体定义直接看代码注释,就不啰嗦了。 这里须要注意的是定义中的名称hello。在第3块模型注册的时候是名称为hello、第4块中函数的初始化名称 即对Python扩大工程中的 编译测试运行编译hello工程(debug版本),在Python调试版本下运行。python调试环境使用 可以看到下面结果,就说明OK了 Microsoft Windows [版本 6.1.7601]
版权所有 (c) 2009 Microsoft Corporation。保存所有权利。
C:UsersCrystalIce>cd /d D:DevMySimplepythonincDebug
D:DevMySimplepythonincDebug>dir
驱动器 D 中的卷是 Docs
卷的序列号是 0002⑵203
D:DevMySimplepythonincDebug 的目录
2016-05⑵8 23:10 <dir> .
2016-05⑵8 23:10 <dir> ..
2016-05⑵8 23:10 639 hello_d.exp
2016-05⑵8 23:10 247,520 hello_d.ilk
2016-05⑵8 23:10 1,718 hello_d.lib
2016-05⑵8 23:10 503,808 hello_d.pdb
2016-05⑵8 23:10 35,840 hello_d.pyd
5 个文件 789,525 字节
2 个目录 27,394,551,808 可用字节
D:DevMySimplepythonincDebug>python_d
Python 3.5.0 (default,Nov 4 2015,21:57:44) [MSC v.1900 32 bit (Intel)] on win32
Type "help","copyright","credits" or "license" for more information.
>>> import hello
>>> print(hello.message('C'))
Hello,C
>>> print(hello.message('module ' + hello.__file__))
Hello,module D:DevMySimplepythonincDebughello_d.pyd
>>> 如果在运行调试中出现下面情况,是Python找不到hello模块致使的。 >>> import hello
Traceback (most recent call last):
File "<stdin>",line 1,in <module>
ImportError: No module named 'hello' 大致缘由: 编译的模块名称有问题,加载不到模块。Python在debug环境下会调用 xxx_d.pyd,release下会调用 xxx.pyd。 创建hellouse.py 调用hello扩大模块在工程中新建1个hellouse.py,用于调度hello扩大模块。
内容以下: """
import and use a C extension library module
www.moguf.com 2016-05⑵8
"""
import hello
print(hello.message('C'))
print(hello.message('module ' + hello.__file__)) 把这个脚本复制到 D:DevMySimplepythonincDebug>python_d hellouse.py
Hello,C
Hello,module D:DevMySimplepythonincDebughello_d.pyd
2.3、集成Python,实现双工先前的的两个示例都是单方面调用,c调用Python 和 Python调用c的扩大模型。并没有交互。在实际工程中不太可能有这类情况,1定是相互交叉调用。 创建duplex工程在VS中创建1个空的控制台程序,并设置Python代码搜索路径,参照2.1。 创建duplex.c 文件这个原文件包括了脚本调用和胶水代码的实现。和2.1、2.2的内容基本1致。其中主要的差异在Python模块的注册上 PyImport_AppendInittab("hello_api",&PyInit_hello_api); 实际对外注册的模块在程序启动时履行,并没有作导出。 文件内容以下。 //
// c API module,test c embedding and extending
//
// www.moguf.com 2016-05⑵9
//
#include <python.h>
#include <string.h>
void helloWorld(char *param)
{
if (param)
printf("It's c,hello %s",param);
else
printf("It's c,hello ");
}
static PyObject *
message(PyObject *self,PyObject *args)
{
char *fromPython;
if (!PyArg_Parse(args,&fromPython))
helloWorld(NULL);
else
helloWorld(fromPython);
return Py_BuildValue("");
}
static PyMethodDef hello_methods[] = {
{ "message",{ NULL,NULL,NULL } // end
};
static struct PyModuleDef hello_api = {
PyModuleDef_HEAD_INIT,"hello_api","mod doc",-1,hello_methods
};
static PyObject*
PyInit_hello_api(void)
{
return PyModule_Create(&hello_api);
}
int main(int argc,char** argv)
{
PyObject* module;
PyObject* func;
// add c api to modules
PyImport_AppendInittab("hello_api",&PyInit_hello_api);
Py_Initialize();
if (!Py_IsInitialized()) {
PyErr_Print();
printf("Couldn't init python");
return -1;
}
module = PyImport_ImportModule("plugins");
if (module) {
func = PyObject_GetAttrString(module,"helloWorld");
if (func && PyCallable_Check(func)) {
PyObject* pArgs = NULL;
PyObject* pReturnVal = PyObject_CallObject(func,pArgs);
}
else {
PyErr_Print();
printf("error: no funcn");
}
Py_XDECREF(func);
Py_DECREF(module);
}
else {
PyErr_Print();
printf("err: no module");
}
Py_Finalize();
return 0;
}
创建脚本plugins.py内容以下 """
Module to test c embedding and extending
www.moguf.com 2016-05⑵9
"""
import hello_api
def helloWorld():
print("it's Python,Hello C")
hello_api.message('python')
return 运行测试在 main函数结束的位置设置断点,这用方便查看结果。运行程序。 it's Python,Hello C
It's c,hello python 可以看到,第1行打印是由Python脚本实现输出,第2行是由python调用程序的API实现打印输出。 3、后续通过上述简单的3个实例实现了c语言和Python脚本的集成。简单、直接和快速的在不同语言直接调度切换控制。 由于Python开始时本身就是基于C写的,所有对c的支持是非常好的。能在c/c++中很方便的进行集成。不过要想更好的实现脚本和C进行交互,那就须要熟习并使用提供的API。 后续将会使用Python脚本作为插件扩大1种模式,在实际工程中实现业务的1些方案。
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |