swig -c++ option breaks wrapper of pure C files with error C3861:unresolved externals

Viewed 54

I am trying to wrap a C API I was given so it can be use in Python using SWIG. This API requires call back functions be passed to it from Python (Python passing a Python function to C which is called from C and executed in Python). This is doable using C++ and directors - %module(directors="1"). However when I add the -c++ option to the swig set-up file it fails to find any of the C functions in the linking stage. It compiles fine and will run in Python, less the needed callbacks, before I add the -c++ option.

I have created a very simple example of this form the demo code:

add.c:

#include <stdio.h>
int add_func(int x, int y) {
    return x+y;
}

add.h:

int add_func(int x, int y);

add.i:

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

setup.py:

from setuptools import setup, Extension

setup(
    name='add',
    py_modules=['add'],
    ext_modules=[
        Extension('_add',
                  sources=['add.c', 'add.i'],
                  swig_opts=['-c++']
        )
    ]
)

compile command:

python3 setup.py build_ext --inplace

with swig_ops=['-c++'] I get the following error:

add_wrap.obj : error LNK2001: unresolved external symbol "int __cdecl add_func(int,int)" (?add_func@@YAHHH@Z)
build\lib.win32-3.8\_add.cp38-win32.pyd : fatal error LNK1120: 1 unresolved externals
error: command 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\BuildTools\\VC\\Tools\\MSVC\\14.16.27023\\bin\\HostX86\\x86\\link.exe' failed with exit status 1120

with swig_ops=['-c++'] removed it compiles fine.

So the question is can you use pure C code with C++ and swig? If so how?

System info:

SWIG Version 4.0.2
Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:43:08) [MSC v.1926 > 32 bit (Intel)] on win32 exe
1 Answers

The generated wrapper is C++ but add.c is compiled as C and has C linkage. The error message indicates it is looking for add_func with C++ linkage.

Either add extern "C" to the add.h prototype so add_wrap.cxx will look for the C function, or rename add.c to add.cpp so it compiles as C++.

A typical way to make a C header both C and C++ compatible is:

#ifdef __cplusplus
extern "C" {
#endif

int add_func(int x, int y);

#ifdef __cplusplus
}
#endif

Another option if you have a C header that you can't change is to wrap its inclusion in extern "C" in the .i file so the generated add_wrap.cxx will use C linkage for the external functions:

%module add
%{
    extern "C" {
        #include "add.h"
    }
%}
%include "add.h"
Related