Search code examples
pythoncswig

SWIG, Python and interface file with inline directive


I have Ubuntu-20.04, Anaconda-3 (installed into the user dir) with Python-3.7.9 and SWIG-4.0.

Here are my files.

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 script 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

After compilation the test script produces

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

However, if I write a longer interface file everything is OK:

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

UPD @Jens suggested to replace # with % in the first (short) interface file. In this case I got

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

Solution

  • %inline both includes the braced code directly in the SWIG-generated wrapper code, but also processes it to generated target language interfaces. So this:

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

    is equivalent to:

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

    But this:

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

    is equivalent to:

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

    To show that %inline is both included and processed, I made:

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

    The above did the right thing and exposed the interface, but it's worse than just using:

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

    %inline is really for inserting new functions into the wrapper and exposing the interface, such as this:

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

    where otherwise you'd have to write, at a minimum:

    %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();
    };