Python调用C/C++

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Python调用C/C++

一、Python调用C动态链接库

Python调用C库比较简单,不经过任何封装打包成so,再使用python的ctypes调用即可。

(1)C语言文件:pycall.c

    /***gcc -o libpycall.so -shared -fPIC pycall.c*/  
    #include <stdio.h>  
    #include <stdlib.h>  
    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"来进行编译。


(3)Python调用动态库的文件:pycall.py

    import ctypes  
    ll = ctypes.cdll.LoadLibrary   
    lib = ll("./libpycall.so")    
    lib.foo(1, 3)  
    print('***finish***')

补充说明:

stdcall调用约定:两种加载方式

Objdll = ctypes.windll.LoadLibrary("dllpath")  
Objdll = ctypes.WinDLL("dllpath") 

cdecl调用约定:也有两种加载方式

Objdll = ctypes.cdll.LoadLibrary("dllpath")  
Objdll = ctypes.CDLL("dllpath")  
#其实windll和cdll分别是WinDLL类和CDll类的对象。

(4)运行结果:

二、Python调用C++(类)动态链接库

需要extern "C"来辅助,也就是说还是只能调用C函数,不能直接调用方法,但是能解析C++方法。不是用extern “C”,构建后的动态链接库没有这些函数的符号表。

(1)C++类文件:pycallclass.cpp

#include <iostream>  
using namespace std;  
  
class TestLib  
{  
    public:  
        void display();  
        void display(int a);  
};
  
void TestLib::display() {  
    cout<<"First display"<<endl;  
}  
  
void TestLib::display(int a) {  
    cout<<"Second display:"<<a<<endl;  
} 
 
extern "C" {  
    TestLib obj;  
 
    void display() {  
        obj.display();   
    } 
 
    void display_int() {  
        obj.display(2);   
    }  
}  

(2)g++编译生成动态库libpycall.so:g++ -o libpycallclass.so -shared -fPIC pycallclass.cpp。

(3)Python调用动态库的文件:pycallclass.py

import ctypes
 
 
def fun1():
    # lib = ctypes.cdll.LoadLibrary("/mnt/hgfs/share/cspace/libpycallclass.so")
    lib = ctypes.CDLL("/mnt/hgfs/share/cspace/libpycallclass.so")
    print(lib.display())
    print(lib.display_int(100))
 

(4)运行结果:

三、Python调用C/C++可执行程序

1)C/C++程序:main.cpp

    #include <iostream>  
    using namespace std;  
    int test()  
    {  
        int a = 10, b = 5;  
        return a+b;  
    } 
 
    int main()  
    {  
        cout<<"---begin---"<<endl;  
        int num = test();  
        cout<<"num="<<num<<endl;  
        cout<<"---end---"<<endl;  
    }  

(2)编译成二进制可执行文件:g++ -o testmain main.cpp。

(3)Python调用程序:main.py

import subprocess  
import os  
main = "./testmain"  
if os.path.exists(main):  
    rc, out = subprocess.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)运行结果:

四、扩展Python(C++为Python编写扩展模块)

所有能被整合或导入到其它python脚本的代码,都可以被称为扩展。可以用Python来写扩展,也可以用C和C++之类的编译型的语言来写扩展。Python在设计之初就考虑到要让模块的导入机制足够抽象。抽象到让使用模块的代码无法了解到模块的具体实现细节。Python的可扩展性具有的优点:方便为语言增加新功能、具有可定制性、代码可以实现复用等。

为 Python 创建扩展需要三个主要的步骤:创建应用程序代码、利用样板来包装代码和编译与测试。

(1)创建应用程序代码和样板代码

#include <Python.h> 
  
int Add(int x, int y) 
{ 
 return x + y; 
} 
  
int Del(int x, int y) 
{ 
 return x - y; 
} 
  
PyObject* WrappAdd(PyObject* self, PyObject* args) 
{ 
 int x, y; 
 if (!PyArg_ParseTuple(args, "ii", &x, &y)) 
 { 
  return NULL; 
 } 
 return Py_BuildValue("i", Add(x, y)); 
} 
  
PyObject* WrappDel(PyObject* self, PyObject* args) 
{ 
 int x, y; 
 if (!PyArg_ParseTuple(args, "ii", &x, &y)) 
 { 
  return NULL; 
 } 
 return Py_BuildValue("i", Del(x, y)); 
} 
static PyMethodDef test_methods[] = { 
 {"Add", WrappAdd, METH_VARARGS, "something"}, 
 {"Del", WrappDel, METH_VARARGS, "something"}, 
 {NULL, NULL} 
}; 
  
static struct PyModuleDef SpamModule = {
    PyModuleDef_HEAD_INIT,
    "t2",   //name of module
    NULL,   //module documentation, may be NULL
    -1,     //size of per-interpreter state of the module, 
            //or -1 if the module keeps state in global variables
    test_methods
};
 
//初始化模块
//PyInit_t2名称  必须跟 t2 库文件名称关联
//可以如下测试:
//import spam 
//dir(spam)
PyMODINIT_FUNC
PyInit_t2(void)
{
    return PyModule_Create(&SpamModule);
}
 

接口的代码被称为“样板”代码,它是应用程序代码与Python解释器之间进行交互所必不可少的一部分。样板主要分为4步:a、包含Python的头文件;b、为每个模块的每一个函数增加一个型如PyObject* Module_func()的包装函数;c、为每个模块增加一个型如PyMethodDef ModuleMethods[]的数组;d、增加模块初始化函数PyMODINIT_FUNC。


Python.h头文件在大多数类Unix系统中会在/usr/local/include/python2.x或/usr/include/python2.x目录中,系统一般都会知道文件安装的路径。

增加包装函数,所在模块名为Extest,那么创建一个包装函数叫Extest_fac(),在Python脚本中使用是先import Extest,然后调用Extest.fac(),当Extest.fac()被调用时,包装函数Extest_fac()会被调用,包装函数接受一个 Python的整数参数,把它转为C的整数,然后调用C的fac()函数,得到一个整型的返回值,最后把这个返回值转为Python的整型数做为整个函数调用的结果返回回去。其他两个包装函数Extest_doppel()和Extest_test()类似。

从Python到C的转换用PyArg_Parse系列函数,int PyArg_ParseTuple():把Python传过来的参数转为C;int PyArg_ParseTupleAndKeywords()与PyArg_ParseTuple()作用相同,但是同时解析关键字参数;它们的用法跟C的sscanf函数很像,都接受一个字符串流,并根据一个指定的格式字符串进行解析,把结果放入到相应的指针所指的变量中去,它们的返回值为1表示解析成功,返回值为0表示失败。从C到Python的转换函数是PyObject Py_BuildValue():把C的数据转为Python的一个对象或一组对象,然后返回之;Py_BuildValue的用法跟sprintf很像,把所有的参数按格式字符串所指定的格式转换成一个Python的对象。

C与Python之间数据转换的转换代码:


为每个模块增加一个型如PyMethodDef ModuleMethods[]的数组,以便于Python解释器能够导入并调用它们,每一个数组都包含了函数在Python中的名字,相应的包装函数的名字以及一个METH_VARARGS常量,METH_VARARGS表示参数以tuple形式传入。 若需要使用PyArg_ParseTupleAndKeywords()函数来分析命名参数的话,还需要让这个标志常量与METH_KEYWORDS常量进行逻辑与运算常量 。数组最后用两个NULL来表示函数信息列表的结束。

所有工作的最后一部分就是模块的初始化函数,以便于解释器能正确的调用模块中的函数。

(2)编译

g++ -fPIC -shared t2.cpp -I/home/liuqz/anaconda3/include/python3.8 -o t2.so

-fPIC:生成位置无关目标代码,适用于动态连接;

-L path:表示在path目录中搜索库文件,如-L.表示在当前目录;

-I path:表示在path目录中搜索头文件;

-o file:制定输出文件为file;

-shared:生成一个共享库文件;

把生成的so放到import能找到的目录(一般放在C:\Python34\DLLs\中)。

备注如果是window下,需要把dll重命名t2.pyd.

运行Python解释器,测试如下

import t2
 
 
def run_t2():
    print(t2.Add(1, 12))
    print(t2.Del(10, 5))

五、Boost::python编写C++扩展模块

5.1 编译boost::python库

5.1.1 下载源码

boost官网下载boost源码,解压到想放到的位置,例如: E:\Learning\Boost\boost_1_69_0

5.1.2 编译boost的lib库

如何系统中只有一个VS,并且Python已加入环境变量,可以切换到boost源码路径下,简单执行以下命令进行编译:

.\bootstrap.bat
.\b2.exe --with-python


可参考:Boost Getting Started on WindowsInstalling Boost.Python on your System

其默认会在boost的stage文件夹下生成静态lib文件。

若需指定参数编译可运行以下命令,查看其帮助文档:

.\b2.exe --help

可参考:vs2017编译boost库 ,解决无法打开文件“libboost_filesystem-vc140-mt-1_58.lib” 问题C++和Python的混合编程-Boost::python的编译和配置


5.2 VS项目配置

  • VS2015建立名为Boost_Python_Sample的Win32的dll工程
  • VS2015也可建立一个Win32 Console Applicantion,然后在VS->Project-
  • >Properties::Genneral::Configuration Type里改成dll

工程设置(Project->Properties->VC++ Directories)

  • Include Diretorise加上Boost根目录
  • Include Diretorise加上Python的include目录
  • Library Diretoties加上boost编译出来的lib目录
  • Library Diretoties加上Python的libs目录

可参考:BOOST的AUTO link机制以及配置Boost库解密——自动链接库(auto_link)C++和Python的混合编程-Boost::python的编译和配置

5.3 源码

(1)因为使用的是静态编译的boost::python库,所以在include头文件之前要加上BOOST_PYTHON_STATIC_LIB,因为在boost::python库的config.hpp中规定,如没定义

BOOST_PYTHON_STATIC_LIB ,则采用动态编译的库

 

#ifdef BOOST_PYTHON_STATIC_LIB
#  define BOOST_PYTHON_STATIC_LINK
# elif !defined(BOOST_PYTHON_DYNAMIC_LIB)
#  define BOOST_PYTHON_DYNAMIC_LIB
#endif

(2)示例代码

#define BOOST_PYTHON_STATIC_LIB
 
#include <boost/python.hpp>
#include <iostream>
#include <string>
 
using namespace std;
using namespace boost::python;
 
struct World
{
  World(std::string msg) : msg(msg) {} // added constructor
  void set(std::string msg) { this->msg = msg; }
  std::string greet() { return msg; }
  std::string msg;
};
 
BOOST_PYTHON_MODULE(bp)
{
  class_<World>("World", init<std::string>())
  //class_<World>("World")
    .def("greet", &World::greet)
    .def("set", &World::set)
    ;
}

可参考:C++和Python的混合编程-Boost::python的编译和配置Boost Exposing ClassesBoost.Python C++导出基本用法

(3)Python_Test_Sample为导出的模块dll,可直接将输出dll文件改成bp.pyd(和BOOST_PYTHON_MODULE(bp)中的bp保持一致),可将pyd文件拷贝到python的库目录下

(python —>lib —>site-packages),或者命令行直接进入pyd所在的目录。

(4)执行Python

新建bp.py,然后执行它,或者命令行执行:

import bp
 
if __name__ == '__main__':
    planet = bp.World('rt')
    s = planet.greet()
    print(s)
    planet.set('howdy')
    s = planet.greet()
    print(s)


参考

1、Python与C/C++的混合调用

2、Python调用C++程序的几种方法

3、Python调用windows下DLL详解 - ctypes库的使用

4、python3 C++扩展

5、python3没有commands模块了

6、Windows下为Python编译C扩展模块

7、Boost Getting Started on Windows 

8、Installing Boost.Python on your System

9、Boost Exposing Classes

10、Boost.Python C++导出基本用法

11、C++和Python的混合编程-Boost::python的编译和配置

12、Boost::Python的安装与调用教程

13、boost github

14、官网Boost.Python

15、vs2017编译boost库 ,解决无法打开文件“libboost_filesystem-vc140-mt-1_58.lib” 问题

16、BOOST的AUTO link机制以及配置

17、Boost库解密——自动链接库(auto_link)

18、C++ Boost库的编译及使用

目录
相关文章
|
6月前
|
人工智能 机器人 C++
【C++/Python】Windows用Swig实现C++调用Python(史上最简单详细,80岁看了都会操作)
【C++/Python】Windows用Swig实现C++调用Python(史上最简单详细,80岁看了都会操作)
136 0
|
6月前
|
编译器 测试技术 C++
【Python 基础教程 01 全面介绍】 Python编程基础全攻略:一文掌握Python语法精髓,从C/C++ 角度学习Python的差异
【Python 基础教程 01 全面介绍】 Python编程基础全攻略:一文掌握Python语法精髓,从C/C++ 角度学习Python的差异
273 0
|
3月前
|
算法框架/工具 C++ Python
根据相机旋转矩阵求解三个轴的旋转角/欧拉角/姿态角 或 旋转矩阵与欧拉角(Euler Angles)之间的相互转换,以及python和C++代码实现
根据相机旋转矩阵求解三个轴的旋转角/欧拉角/姿态角 或 旋转矩阵与欧拉角(Euler Angles)之间的相互转换,以及python和C++代码实现
237 0
|
5月前
|
Ubuntu C++ Docker
Docker的基本指令和HTML/PYTHON/C++的简单创建示例
Docker的基本指令和HTML/PYTHON/C++的简单创建示例
|
4月前
|
编译器 开发工具 C++
【Python】已解决error: Microsoft Visual C++ 14.0 or greater is required. Get it with “Microsoft C++ Build
【Python】已解决error: Microsoft Visual C++ 14.0 or greater is required. Get it with “Microsoft C++ Build
1998 0
|
1月前
|
C++ Python
探索Python与C/C++混合编程的艺术
探索Python与C/C++混合编程的艺术
37 1
WK
|
2月前
|
机器学习/深度学习 Java 程序员
为什么Python比C++慢很多?
Python相较于C++较慢主要体现在:动态类型系统导致运行时需解析类型,增加开销;作为解释型语言,逐行转换字节码的过程延长了执行时间;自动内存管理和垃圾回收机制虽简化操作但也带来了额外负担;全局解释器锁(GIL)限制了多线程性能;尽管Python库方便灵活,但在性能上往往不及C++底层库。然而,Python在某些领域如数据分析、机器学习中,凭借其高级别抽象和简洁语法仍表现出色。选语言需依据具体应用场景和需求综合考量。
WK
74 1
|
3月前
|
PHP C++ Python
右手坐标系,空间点绕轴旋转公式&程序(Python和C++程序)
右手坐标系,空间点绕轴旋转公式&程序(Python和C++程序)
64 0
WK
|
3月前
|
机器学习/深度学习 运维 Java
Python 相对于 C++ 有哪些明显的优势
C++是一种强大且高效的编程语言,被广泛应用在系统软件、游戏开发、嵌入式系统等多个领域。然而Python在某些方面展现出显著优势:Python语法简洁直观,易于学习与使用,提高了代码的可读性和团队协作效率;拥有丰富的第三方库和框架资源,能有效提升开发效率;具备良好的跨平台性,无需大量修改即可适应不同操作系统;
WK
53 0
|
4月前
|
算法 Java C++
C++和Python在内存管理上的主要区别是什么?
【7月更文挑战第2天】C++和Python在内存管理上的主要区别是什么?
116 1