SWIG、Python 和带有内联指令的接口文件

SWIG, Python and interface file with inline directive

我有 Ubuntu-20.04、Anaconda-3(安装在用户目录中)以及 Python-3.7.9 和 SWIG-4.0。

这是我的文件。

a.h:

void foo(void);

a.c:

#include <stdio.h>
#include "a.h"
void foo(void) { printf("Foo!\n"); }

a.i:

%module a
%inline %{
#include "a.h"
%}

test.py:

import a
a.foo()

编译脚本compile.sh:

A=$HOME/opt/anaconda3
I=$A/include/python3.7m
gcc -c -fpic -DHAVE_CONFIG_H -I$I a.c
$A/bin/swig -python -py3 a.i
gcc -c -fpic -DHAVE_CONFIG_H -I$I a_wrap.c
gcc -shared a.o a_wrap.o -o _a.so

编译后测试脚本生成

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    a.foo()
AttributeError: module 'a' has no attribute 'foo'

但是,如果我写一个更长的接口文件一切都OK:

%module a
%{
#include "a.h"
%}
%include "a.h"

UPD @Jens 建议在第一个(短)接口文件中将 # 替换为 %。在这种情况下,我得到了

a_wrap.c:2701:1: error: expected identifier or '(' before '%' token
 2701 | %include "a.h"
      | ^
a_wrap.c:2720:12: error: '_wrap_foo' undeclared here (not in a function)
 2720 |   { "foo", _wrap_foo, METH_NOARGS, NULL},
      |            ^~~~~~~~~
gcc: error: a_wrap.o: No such file or directory

%inline 既在 SWIG 生成的包装器代码中直接包含大括号代码,又将其处理为生成的目标语言接口。所以这个:

%module a
%inline %{
void foo(void);
%}

相当于:

%module a
%{
void foo(void) {}
%}
void foo(void) {}

但是这个:

%module a
%inline %{
#include "a.h"
%}

相当于:

%module a
%{
#include "a.h"
%}
#include "a.h"  // NOT the same as %include "a.h" and isn't processed for interfaces

为了显示 %inline 被包含和处理,我做了:

%module a
%inline %{
#include "a.h"   // ignored in "include-in-wrapper" pass
#ifdef SWIG
%include "a.h"   // processed in "wrap-with-SWIG" pass
#endif
%}

上面的做法是对的,暴露了接口,但是比只用还差:

%module a
%{
#include "a.h"
%}
%include "a.h"

%inline其实就是为了往wrapper中插入新的函数,暴露接口,比如这样:

%module a
%inline %{
class myClass
{
private: 
  int a;
public: 
  myClass(){} 
  void foo(){}
};
%}

否则你至少要写:

%module a

%{ // new functionality added to wrapper
class myClass
{
private: 
  int a;
public: 
  myClass(){} 
  void foo(){}
};
%}

class myClass   // process the interface
{
public:         // note don't need to repeat private part or
  myClass();    // implementation, just public declarations to be exposed.
  void foo();
};