Reputation: 385
I'm currently learning how to create C extensions for Python so that I can call C/C++ code. I've been teaching myself with a few examples. I started with this guide and it was very helpful for getting up and running. All of the guides and examples I've found online only give C code where a single function is defined. I'm planning to access a C++ library with multiple functions from Python and so I decided the next logical step in learning would be to add more functions to the example.
However, when I do this, only the first function in the extension is accessible from Python. Here's the example that I've made for myself (for reference I'm working on Ubuntu 21):
The C code (with two functions: func1
and func2
, where func1
also depends on func2
) and header files:
//func1.cpp
extern "C"
#include "func1.h"
double func1(double x, double y, double z) {
return x*x + y*y + z*z*z + func2(x,y,z);
}
double func2(double x, double y, double z) {
return x*y*z;
}
//func1.h
double func1(double x, double y, double z);
double func2(double x, double y, double z);
Then the Python extension setup which is run with python setup.py build
:
#setup.py
from setuptools import setup, Extension
setup(
ext_modules=[Extension('func1', sources=['func1.cpp'], include_dirs=['func1.h'])]
)
And finally the Python script where the extension is used which is run with python example.py
:
#example.py
import ctypes
import numpy
import glob
libfile = glob.glob('build/*/func1*.so')[0]
lib = ctypes.CDLL(libfile)
func1 = lib.func1
func2 = lib.func2
func1.restype = ctypes.c_double
func1.argtypes = [ctypes.c_double, ctypes.c_double, ctypes.c_double]
func2.restype = ctypes.c_double
func2.argtypes = [ctypes.c_double, ctypes.c_double, ctypes.c_double]
print( func1(2,3,4) )
print( func2(2,3,4) )
When I do this example, python setup.py build
compiles fine. However, when I get run the example file with python example.py
I get that following error and traceback:
Traceback (most recent call last):
File "~/Desktop/c_extension_example/example.py", line 12, in <module>
func2 = lib.func2
File "/usr/lib/python3.9/ctypes/__init__.py", line 387, in __getattr__
func = self.__getitem__(name)
File "/usr/lib/python3.9/ctypes/__init__.py", line 392, in __getitem__
func = self._FuncPtr((name_or_ordinal, self))
AttributeError: build/lib.linux-x86_64-3.9/func1.cpython-39-x86_64-linux-gnu.so: undefined symbol: func2
Which indicates that the function func2
isn't accessible through the extension. However, if I remove the references to func2
in example.py
then everything runs fine and I get a result from func1
that is correct.
This is interesting to me because func1
depends on func2
, but I can't access func2
directly. How can I change this example such that I can also access func2
? Do I need to make a separate file and extension for each function that I want to access? That would be a bit cumbersome if I want to make an extension for a large C library.
Any help is greatly appreciated!
Thanks!
Upvotes: 3
Views: 384
Reputation: 3081
Make export "C"
include both functions:
#include "func1.h"
extern "C" {
double func1(double x, double y, double z) {
return x*x + y*y + z*z*z + func2(x,y,z);
}
double func2(double x, double y, double z) {
return x*y*z;
}
}
extern "C" {
double func1(double x, double y, double z);
double func2(double x, double y, double z);
}
Without the braces, extern "C"
is only applied to the next declaration, which is double func1(double x, double y, double z);
.
Upvotes: 2