在无法访问库源的情况下使用 swig c++ 到 python

Posted

技术标签:

【中文标题】在无法访问库源的情况下使用 swig c++ 到 python【英文标题】:Using swig c++ to python with no access to library source 【发布时间】:2019-01-14 19:20:32 【问题描述】:

我是 swig 的新手,并尝试在文档中搜索答案,但我要么错过了答案,要么在看到答案时没有认出答案。

我有一个来自第三方的大型 C++ 库,除了标头之外我没有源代码。我需要从 python 访问一些 API,其中一些 API 的函数原型类似于:

int foo(int& a, int& b, int& c);

我创建了一个 .i 文件,如下所示:

%module myWrapper 
%   
  #include libraryHeader.h
%
extern int foo(int& m1, int& m2, int& bf);

这通过 swig、g++ 编译器和链接器没有问题。我可以将模块导入 python,但是当我调用它时,我收到以下错误:

TypeError:在方法“foo”中,类型为“int &”的参数 1

我已经声明了 3 个 int 变量,例如 m1 = 0; m2 = 0; bf = 0 所以我传入了 something。有没有办法使用 typemaps.i 库或显式使用 typemaps 来做到这一点?文档在这个主题上似乎有点含糊。

提前致谢, 保罗

【问题讨论】:

您所做的一切都是正确的。这里唯一的复杂之处是您有一个非构造引用,需要一些特殊处理才能使其正常工作。只要您了解语义,这些都与缺乏源代码无关。 大家好,感谢您的帮助。我能够解决我的问题,并且我已经在下面回答了我自己的问题...... 【参考方案1】:

SWIG 需要提示,其中参数不仅仅是输入。这些提示由类型映射提供。您要么需要定义类型映射,要么使用预定义的类型映射(参见typemaps.i)。

这是一个例子:

%module test

%
    int foo(int& a, int& b, int& c)
    
        int ret = a + b + c;
        a = 10;
        b = 20;
        c = 30;
        return ret;
    
%

%include <typemaps.i>
int foo(int& INOUT, int& INOUT, int& INOUT);

使用INOUT 类型映射,Python 可以将整数传递给带有int*int&amp; 的函数,返回值将是原始返回值和任何输出参数的元组。编译上面的 SWIG 结果并在 Python 中使用如下所示:

>>> import test
>>> test.foo(1,2,3)
[6, 10, 20, 30]

您可以使用如下代码更新变量:

>>> a,b,c = 1,2,3
>>> r,a,b,c = test.foo(a,b,c)
>>> r
6
>>> a,b,c
(10, 20, 30)

请注意,您也可以将现有类型映射 %apply 用于类型,因此如果使用 %include "libraryHeader.h" 而不是直接声明函数,您可以使用以下内容将 INOUT 类型映射一般应用于所有 int&amp; 参数:

%apply int &INOUT  int& ;
%include "libraryHeader.h"

【讨论】:

谢谢@Mark。这帮助我找到了问题的答案【参考方案2】:

在其他人的帮助下,我已经回答了我自己的问题。我创建了一个 .i 文件,如下所示:

%module myWrapper

%include "typemaps.i"

%
 #include "libraryHeader.h"
 typedef unsigned int MY_RESULT;
%

typedef unsigned int MY_RESULT;
extern MY_RESULT MyGetVersion(int& OUTPUT, int& OUTPUT, int& OUTPUT, int& 
OUTPUT);
extern MY_RESULT MyGetDeviceDriverVersion(int deviceType, int& OUTPUT, int& 
OUTPUT, int& OUTPUT, int& OUTPUT);

它干净利落地进行、编译和链接,并且一个非常简单的 python 测试脚本返回了预期的结果。脚本如下:

#!/usr/bin/python3

import myWrapper

ret = 0    # function return value
maj = 0    # major version
min = 0    # minor version
bugFix = 0 # bug fix number
build = 0  # build

#
# Because of the typemap defined in myWrapper.i, we don't need to supply function arguments. 
# The results are returned by the function call as seen below...
#
ret, maj, min, bugFix, build = myWrapper.MyGetVersion()

#
# See what we got 
#
print('function return: '+str(ret)+' Maj: '+str(maj)+' Min: '+str(min)+' BugFix:'+str(bugFix)+' Build: '+str(build))

#
# This call returns the same information that the call above does but adds an input argument
# that specifies the Device Catagory. 0 is PCI
#
ret, maj, min, bugFix, build = myWrapper.MyGetDeviceDriverVersion(0)

print('function return: '+str(ret)+' Maj: '+str(maj)+' Min: '+str(min)+' BugFix: '+str(bugFix)+' Build: '+str(build))

运行此脚本的结果提供了预期的结果:

function return: 0 Maj: 5 Min: 31 BugFix: 0 Build: 109
function return: 0 Maj: 4 Min: 26 BugFix: 4 Build: 253

非常感谢大家的帮助,我希望这个答案可以帮助其他人。

保罗

【讨论】:

以上是关于在无法访问库源的情况下使用 swig c++ 到 python的主要内容,如果未能解决你的问题,请参考以下文章

如何将 C# 字符串 [] 编组(理想情况下在 SWIG 中)到 C++ 字符串*?

SWIG C++ 预编译 DLL

使用 SWIG 在 Python 中访问 C++ typedef

SWIG:无法使用双指针访问构造函数

无法使用 SWIG 在 Python 中实例化 C++ 类(获取属性错误)

SWIG:使用带有 shared_ptr 的 std::map 访问器?