实例:Python调用c++文件(参数为指针和数组指针)

本文作为Python调用c++的进阶实现,简单的实现参考文章Python调用c++高级(swig)

1 一个小小的工作目标

通过Python调用c++文件,生成一个序列,包含10个随机的(0,1)序列,涉及到python为c++函数提供指针型变量和数组型变量

2 c++实现

c++实现的头文件:

//DataGen.h
#ifndef DATAGEN_H
#define DATAGEN_H

#include 
#include 
#define FrameLen 1024
#define MASK 12349876
#define IA 16807
#define IM 2147483647
#define AM (1.0/IM)
#define IQ 127773
#define IR 2836

//#include "101_UserType.h"
//#include "102_UserDefine.h"

double ran0(long *idum);
double GaussNoise(double Sigma, long *idum);
void randombit_gene(int *output, int length, long *seed);
int randint(long *idum);

#endif

c++实现的源文件:

//DataGen.cpp
#include "DataGen.h"
//函数实现

c++实现生成长度为10的int型数组:

#include 
#include "../DataGen.h"
using namespace std;
long SeEd=1234;

int main()
{
    int *SourceBit = new int[FrameLen];
    randombit_gene(SourceBit, FrameLen, &SeEd);
    delete [] SourceBit;
    return 0;
}

实现过程为:

  1. 生成一个long*的指针作为随机产生器的种子;
  2. 初始化生成一个长度为FrameLen的int型数组SourceBits;
  3. 调用c++函数randombit_gene(SourceBits, FrameLen, &SeEd);

3 Python中调用randombit_gene函数

先直接给出swig配置文件(之后使用命令行生成动态链接库.so文件)和Python执行文件

/* File: DataGen.i */
%module DataGen

//用来生成指针
%include "cpointer.i"
//用来生成数组
%include "carrays.i"

%pointer_class(long, long_p);
%array_class(int, intArray);

%include "DataGen.h"

%{
#include "DataGen.h"
%}

命令行生成动态链接库.so文件,生成后就可以import了

$swig -c++ -python DataGen.i
$g++ -fPIC -shared DataGen_wrap.cxx DataGen.cpp -o _DataGen.so -I/usr/include/python2.7/ -lpython2.7

Python执行文件调用randombit_gene函数并输出:

#DataGen.py
import DataGen
# FrameLen = 10
seed = DataGen.long_p()
DataGen.long_p.assign(seed,123453L)
SourceBits = DataGen.intArray(10)
DataGen.randombit_gene(SourceBits, 10, seed)
for i in range(10):
    print i,SourceBits[i]

执行后输出为:

0 0
1 0
2 1
3 1
4 0
5 1
6 1
7 1
8 0
9 1

4 swig配置文件.i解释

Python产生可以由c++使用的指针和数组,这里需要使用swig中提供的两个库:

  • cpointer.i
  • carray.i

swig通过这两个库便可以使python可以产生指针和数组(都是地址字符串,Python本身无法使用,只是作为参数传递给c++库的接口(swig编辑了该接口),然后swig编辑的接口将该参数翻译(cpointer.i和carray.i)为c++地址,由c++库使用。

以下内容翻译自swig官方文档:

4.1 cpointer.i使Python可以产生指针

cpointer.i模块定义了可以用于用来产生围绕简单C指针包装宏。此模块的主要用途是生成指向原始数据类型(如int和double)的指针 。
在swig配置文件.i添加如下指令就可以调用cpointer.i中的函数

%include "cpointer.i"

见代码中第二行:

/* File: DataGen.i */
%module DataGen

//用来生成指针
%include "cpointer.i"
//用来生成数组
%include "carrays.i"

%pointer_class(long, long_p);
%array_class(int, intArray);

%include "DataGen.h"

%{
#include "DataGen.h"
%}

cpointer,i库下有两个函数可以调用

  • %pointer_functions(type, name);
  • %pointer_class(type, name);

这两个函数都可以实现指针的生成,后一种简单但只能针对简单指针类型(基本类型)

4.1.1 %pointer_functions(type, name)

生成一个用于处理指针类型的四个函数的集合:

type *new_name()
//创建一个新的type类型的对象并返回一个指向它的指针。在C中,该对象是使用calloc()创建的。在C ++中,使用new创建的。

type *copy_name(type value)
//创建一个新的type类型的对象并返回一个指向它的指针。初始值是通过从值复制来设置的。在C中,该对象是使用calloc()创建的。在C ++中,使用新的。

type *delete_name(type *obj)
//Deletes an object type type.

void name_assign(type *obj, type value)
//赋值 *obj = value.

type name_value(type *obj)
//Returns the value of *obj

使用此宏时,type可以是任何类型,name必须是目标语言的合法标识符。 name不应该与接口文件中使用的任何其他名称相对应。

这是一个使用%pointer_functions()的简单例子:

module example 
%include“cpointer.i” 

/*创建一些函数来处理"int *"*/ 
%pointer_functions(int,intp); 

/*使用"int *"的函数*/ 
void add(int x,int y,int * result);

现在,在Python中:

import example
c = example.new_intp()#为存储结果创建一个“int”
example.add(3,4,c)#调用函数
example.intp_value(c)#解除引用
7
example.delete_intp(c)#删除

4.1.2 %pointer_class(type,name)

在基于类的界面中 包装一个type*的指针。这个接口如下:

struct name {
   name();                            // Create pointer object
  ~name();                            // Delete pointer object
   void assign(type value);           // Assign value
   type value();                      // Get value
   type *cast();                      // Cast the pointer to original type
   static name *frompointer(type *);  // Create class wrapper from existing
                                      // pointer
};

当使用这个宏时,type被限制在一个简单的类型名称,比如int,float或者Foo。指针和其他复杂的类型是不允许的。 名称必须是尚未使用的有效标识符。当一个指针被封装为一个类时,“类”可以被透明地传递给任何需要该指针的函数。

如果目标语言不支持代理类,则使用此宏将产生与%pointer_functions()宏相同的函数示例。

应该注意的是,类接口确实引入了一个新的对象,或者将一个指针包装在一个特殊的结构中。相反,原始指针直接使用。
下面是使用一个类的例子:

%模块示例
%include“cpointer.i” 

/* Wrap a class interface around an "int *" */%
%pointer_class(int, intp);

/* A function that uses an "int *" */
void add(int x, int y, int *result);

现在,在Python(使用代理类)

import example
c = example.intp()#为存储结果创建一个“int”
example.add(3,4,c)#调用函数
c.value()#Dereference
7

在这两个宏中,当使用简单的指针时,%pointer_class可能是最方便的。这是因为指针是类似于对象的访问方式,并且可以很容易地进行垃圾收集(销毁指针对象破坏了底层对象)。

4.2 carrays.i使Python可以产生数组指针

这个模块定义了一些宏,这些宏有助于将普通的C指针作为数组来包装。该模块不提供任何安全性或额外的包装层 - 它仅提供用于创建,销毁和修改原始C数组数据的内容的功能。

在swig配置文件.i添加如下指令就可以调用cpointer.i中的函数

%include "carrays.i"

见代码中第三行:

/* File: DataGen.i */
%module DataGen

//用来生成指针
%include "cpointer.i"
//用来生成数组
%include "carrays.i"

%pointer_class(long, long_p);
%array_class(int, intArray);

%include "DataGen.h"

%{
#include "DataGen.h"
%}

cpointer,i库下有两个函数可以调用

  • %array_functions(type,name);
  • %array_class(type,name);

4.2.1 array_functions(type,name)

array_functions(type,name)将会产生4个函数:

type *new_name(int nelements)
//创建类型的对象的一个新的type型数组。在C中,数组是使用calloc()分配的 。在C ++中,使用new []。

type *delete_name(type *ary)
//删除一个数组。在C中,使用free()。在C ++中,使用delete []。

type name_getitem(type *ary, int index)
//返回值 ary [index]。

void name_setitem(type *ary, int index, type value)
//赋值 ary[index] = value.

使用此宏时,type可以是任何类型,name必须是目标语言的合法标识符。name不应该与接口文件中使用的任何其他名称相对应。

这里是一个%array_functions()的例子。假设你有这样的功能:

void print_array(double x[10]) {
   int i;
   for (i = 0; i < 10; i++) {
      printf("[%d] = %g\n", i, x[i]);
   }
}

swig配置文件时,可以这样写:

%module example

%include "carrays.i"
%array_functions(double, doubleArray);

void print_array(double x[10]);

现在,在Python中

import example 
c = example.new_doubleArray(10)             # Create an array
for i in range(0,10):
    example.doubleArray_setitem(a,i,2*i)    # Set a value
print_array(c)                              # Pass to C 
example.delete_doubleArray(a)               # Destroy array

4.2.2 array_class(type,name)

在基于类的界面中 包装一个type*型的指针。这个接口如下:

struct name {
   name(int nelements);                  // Create an array
  ~name();                               // Delete array
   type getitem(int index);              // Return item
   void setitem(int index, type value);  // Set item
   type *cast();                         // Cast to original type
   static name *frompointer(type *);     // Create class wrapper from
                                         // existing pointer
};

当使用这个宏时,type被限制为一个简单的类型名称,比如int或者float。指针和其他复杂的类型是不允许的。 name必须是尚未使用的有效标识符。当一个指针被封装为一个类时,它可以透明地传递给任何需要该指针的函数。

与代理类结合使用时,%array_class()宏特别有用。例如:

module example 
%include“carrays.i” 
%array_class(double,doubleArray); 

void print_array(double x [10]);

允许你这样做:

import example
c = example.doubleArray(10)  # Create double[10]
for i in range(0,10):
    c[i] = 2*i               # Assign values,可以直接访问c[i]
example.print_array(c)       # Pass to C

你可能感兴趣的:(实例:Python调用c++文件(参数为指针和数组指针))