Python模块和C/C++的动态库间相互调用在实际的应用中会有所涉及,在此作一总结。
Python调用C库比较简单,不经过任何封装打包成so,再使用python的ctypes调用即可。
(1)C语言文件:pycall.c
/***gcc -o libpycall.so -shared -fPIC pycall.c*/
#include
#include
int foo(int a, int b)
{
printf("you input %d and %d\n", a, b);
return a+b;
}
(2)gcc编译生成动态库libpycall.so:gcc -o libpycall.so -shared -fPIC pycall.c。使用g++编译生成C动态库的代码中的函数或者方法时,需要使用extern "C"来进行编译。
import ctypes
ll = ctypes.cdll.LoadLibrary
lib = ll("./libpycall.so")
lib.foo(1, 3)
print '***finish***'
(4)运行结果:
#include
using namespace std;
class TestLib
{
public:
void display();
void display(int a);
};
void TestLib::display() {
cout<<"First display"<
(2)g++编译生成动态库libpycall.so:g++ -o libpycallclass.so -shared -fPIC pycallclass.cpp。import ctypes
so = ctypes.cdll.LoadLibrary
lib = so("./libpycallclass.so")
print 'display()'
lib.display()
print 'display(100)'
lib.display_int(100)
(4)运行结果:
#include
using namespace std;
int test()
{
int a = 10, b = 5;
return a+b;
}
int main()
{
cout<<"---begin---"<
(2)编译成二进制可执行文件:g++ -o testmain main.cpp。
import commands
import os
main = "./testmain"
if os.path.exists(main):
rc, out = commands.getstatusoutput(main)
print 'rc = %d, \nout = %s' % (rc, out)
print '*'*10
f = os.popen(main)
data = f.readlines()
f.close()
print data
print '*'*10
os.system(main)
(4)运行结果:
#include
#include
#include
int fac(int n)
{
if (n < 2) return(1); /* 0! == 1! == 1 */
return (n)*fac(n-1); /* n! == n*(n-1)! */
}
char *reverse(char *s)
{
register char t, /* tmp */
*p = s, /* fwd */
*q = (s + (strlen(s) - 1)); /* bwd */
while (p < q) /* if p < q */
{
t = *p; /* swap & move ptrs */
*p++ = *q;
*q-- = t;
}
return(s);
}
int main()
{
char s[BUFSIZ];
printf("4! == %d\n", fac(4));
printf("8! == %d\n", fac(8));
printf("12! == %d\n", fac(12));
strcpy(s, "abcdef");
printf("reversing 'abcdef', we get '%s'\n", \
reverse(s));
strcpy(s, "madam");
printf("reversing 'madam', we get '%s'\n", \
reverse(s));
return 0;
}
上述代码中有两个函数,一个是递归求阶乘的函数fac();另一个reverse()函数实现了一个简单的字符串反转算法,其主要目的是修改传入的字符串,使其内容完全反转,但不需要申请内存后反着复制的方法。
#include
#include
#include
int fac(int n)
{
if (n < 2) return(1);
return (n)*fac(n-1);
}
char *reverse(char *s)
{
register char t,
*p = s,
*q = (s + (strlen(s) - 1));
while (s && (p < q))
{
t = *p;
*p++ = *q;
*q-- = t;
}
return(s);
}
int test()
{
char s[BUFSIZ];
printf("4! == %d\n", fac(4));
printf("8! == %d\n", fac(8));
printf("12! == %d\n", fac(12));
strcpy(s, "abcdef");
printf("reversing 'abcdef', we get '%s'\n", \
reverse(s));
strcpy(s, "madam");
printf("reversing 'madam', we get '%s'\n", \
reverse(s));
return 0;
}
#include "Python.h"
static PyObject *
Extest_fac(PyObject *self, PyObject *args)
{
int num;
if (!PyArg_ParseTuple(args, "i", &num))
return NULL;
return (PyObject*)Py_BuildValue("i", fac(num));
}
static PyObject *
Extest_doppel(PyObject *self, PyObject *args)
{
char *orig_str;
char *dupe_str;
PyObject* retval;
if (!PyArg_ParseTuple(args, "s", &orig_str))
return NULL;
retval = (PyObject*)Py_BuildValue("ss", orig_str,
dupe_str=reverse(strdup(orig_str)));
free(dupe_str); #防止内存泄漏
return retval;
}
static PyObject *
Extest_test(PyObject *self, PyObject *args)
{
test();
return (PyObject*)Py_BuildValue("");
}
static PyMethodDef
ExtestMethods[] =
{
{ "fac", Extest_fac, METH_VARARGS },
{ "doppel", Extest_doppel, METH_VARARGS },
{ "test", Extest_test, METH_VARARGS },
{ NULL, NULL },
};
void initExtest()
{
Py_InitModule("Extest", ExtestMethods);
}
Python.h头文件在大多数类Unix系统中会在/usr/local/include/python2.x或/usr/include/python2.x目录中,系统一般都会知道文件安装的路径。
#!/usr/bin/env python
from distutils.core import setup, Extension
MOD = 'Extest'
setup(name=MOD, ext_modules=[Extension(MOD, sources=['Extest2.c'])])
Extension()第一个参数是(完整的)扩展的名字,如果模块是包的一部分的话,还要加上用'.'分隔的完整的包的名字。上述的扩展是独立的,所以名字只要写"Extest"就行;sources参数是所有源代码的文件列表,只有一个文件Extest2.c。setup需要两个参数:一个名字参数表示要编译哪个内容;另一个列表参数列出要编译的对象,上述要编译的是一个扩展,故把ext_modules参数的值设为扩展模块的列表。
creating build/lib.linux-x86_64-2.6
gcc -pthread -shared build/temp.linux-x86_64-2.6/Extest2.o -L/usr/lib64 -lpython2.6 -o build/lib.linux-x86_64-2.6/Extest.so
(4)导入和测试
C++可以调用Python脚本,那么就可以写一些Python的脚本接口供C++调用了,至少可以把Python当成文本形式的动态链接库,
需要的时候还可以改一改,只要不改变接口。缺点是C++的程序一旦编译好了,再改就没那么方便了。
(1)Python脚本:pytest.py
#test function
def add(a,b):
print "in python function add"
print "a = " + str(a)
print "b = " + str(b)
print "ret = " + str(a+b)
return
def foo(a):
print "in python function foo"
print "a = " + str(a)
print "ret = " + str(a * a)
return
class guestlist:
def __init__(self):
print "aaaa"
def p():
print "bbbbb"
def __getitem__(self, id):
return "ccccc"
def update():
guest = guestlist()
print guest['aa']
#update()
(2)C++代码:
/**g++ -o callpy callpy.cpp -I/usr/include/python2.6 -L/usr/lib64/python2.6/config -lpython2.6**/
#include
int main(int argc, char** argv)
{
// 初始化Python
//在使用Python系统前,必须使用Py_Initialize对其
//进行初始化。它会载入Python的内建模块并添加系统路
//径到模块搜索路径中。这个函数没有返回值,检查系统
//是否初始化成功需要使用Py_IsInitialized。
Py_Initialize();
// 检查初始化是否成功
if ( !Py_IsInitialized() ) {
return -1;
}
// 添加当前路径
//把输入的字符串作为Python代码直接运行,返回0
//表示成功,-1表示有错。大多时候错误都是因为字符串
//中有语法错误。
PyRun_SimpleString("import sys");
PyRun_SimpleString("print '---import sys---'");
PyRun_SimpleString("sys.path.append('./')");
PyObject *pName,*pModule,*pDict,*pFunc,*pArgs;
// 载入名为pytest的脚本
pName = PyString_FromString("pytest");
pModule = PyImport_Import(pName);
if ( !pModule ) {
printf("can't find pytest.py");
getchar();
return -1;
}
pDict = PyModule_GetDict(pModule);
if ( !pDict ) {
return -1;
}
// 找出函数名为add的函数
printf("----------------------\n");
pFunc = PyDict_GetItemString(pDict, "add");
if ( !pFunc || !PyCallable_Check(pFunc) ) {
printf("can't find function [add]");
getchar();
return -1;
}
// 参数进栈
PyObject *pArgs;
pArgs = PyTuple_New(2);
// PyObject* Py_BuildValue(char *format, ...)
// 把C++的变量转换成一个Python对象。当需要从
// C++传递变量到Python时,就会使用这个函数。此函数
// 有点类似C的printf,但格式不同。常用的格式有
// s 表示字符串,
// i 表示整型变量,
// f 表示浮点数,
// O 表示一个Python对象。
PyTuple_SetItem(pArgs, 0, Py_BuildValue("l",3));
PyTuple_SetItem(pArgs, 1, Py_BuildValue("l",4));
// 调用Python函数
PyObject_CallObject(pFunc, pArgs);
//下面这段是查找函数foo 并执行foo
printf("----------------------\n");
pFunc = PyDict_GetItemString(pDict, "foo");
if ( !pFunc || !PyCallable_Check(pFunc) ) {
printf("can't find function [foo]");
getchar();
return -1;
}
pArgs = PyTuple_New(1);
PyTuple_SetItem(pArgs, 0, Py_BuildValue("l",2));
PyObject_CallObject(pFunc, pArgs);
printf("----------------------\n");
pFunc = PyDict_GetItemString(pDict, "update");
if ( !pFunc || !PyCallable_Check(pFunc) ) {
printf("can't find function [update]");
getchar();
return -1;
}
pArgs = PyTuple_New(0);
PyTuple_SetItem(pArgs, 0, Py_BuildValue(""));
PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pName);
Py_DECREF(pArgs);
Py_DECREF(pModule);
// 关闭Python
Py_Finalize();
return 0;
}
(3)C++编译成二进制可执行文件:g++ -o callpy callpy.cpp -I/usr/include/python2.6 -L/usr/lib64/python2.6/config -lpython2.6,编译选项需要手动指定Python的include路径和链接接路径(Python版本号根据具体情况而定)。