Now that I can call a C function in Python, I'll write a note. There are several ways to create a C wrapper, but this time I chose Boost.Python.
I installed it referring to this article. The procedure is as follows. (Mac environment, Python 3.7)
brew install boost-python3
Once installed, link to the directory that runs Boost.Python's dylib and Python itself in / usr / local / Cellar /
.
ln -s /usr/local/Cellar/boost-python3/1.71.0_1/lib/libboost_python37.dylib /path/to/directory/libboost_python37.dylib
ln -s /usr/local/Cellar/python/3.7.5/Frameworks/Python.framework/Versions/3.7/Python /path/to/directory/libpython3.7.dylib
pyconfig.h
cannot be includedWhen first compiled
I got a fatal error: pyconfig.h: No such file or directory
, so I was able to solve it by following the steps below.
(1) Check the path of Python includes
python3.7-config --includes --libs
(2) The following information will appear. Choose the first (this example is -I / usr / local / Cellar / python / 3.7.5 / Frameworks / Python.framework / Versions / 3.7 / include / python3.7m
)
-I/usr/local/Cellar/python/3.7.5/Frameworks/Python.framework/Versions/3.7/include/python3.7m -I/home/victor/anaconda3/include/python3.7m -lpython3.7m -lpthread -ldl -lutil -lrt -lm
(3) Add the following line to ~ / .bashrc
with the path excluding -I
:
~/.bashrc
export CPLUS_INCLUDE_PATH="$CPLUS_INCLUDE_PATH:/usr/local/Cellar/python/3.7.5/Frameworks/Python.framework/Versions/3.7/include/python3.7m"
(4) Reflect
source ~/.bashrc
Hello World Boost.Python is a C ++ library, so when using C, prepare C ++ separately and include the contents of C. Prepare C, C ++, Python files and Makefile:
my_c.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void hello_world();
my_c.c
#include "my_c.h"
void hello_world() {
printf("hello world\n");
}
my_cpp.cpp
#include <boost/python.hpp>
extern "C" {
#include "my_c.h"
}
using namespace boost::python;
BOOST_PYTHON_MODULE(my_wrapper){
def("hello_world", hello_world);
}
my_python.py
import my_wrapper
my_wrapper.hello_world()
Makefile
wrapper: my_cpp.cpp
gcc -c -o my_c.o my_c.c
g++ -c -o my_cpp.o my_cpp.cpp
g++ -fPIC -Wall -O2 -shared -o my_wrapper.so my_c.o my_cpp.o libboost_python37.dylib libpython3.7.dylib
You can do it with this
$ make
$ python my_python.py
hello world
If it is a simple function, no other processing is required, but if it is a type that does not know each other, conversion seems necessary. It will be sample code instead of explanation.
my_c.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int a;
int b;
}my_struct;
my_struct my_sum(my_struct *my_array, int len);
my_c.c
#include "my_c.h"
my_struct my_sum(my_struct *my_array, int len) {
my_struct sum = { 0 };
for(int i = 0; i < len; i++) {
sum.a += my_array[i].a;
sum.b += my_array[i].b;
}
return sum;
}
my_cpp.cpp
#include <boost/python.hpp>
extern "C" {
#include "my_c.h"
}
using namespace boost::python;
// boost::python::dict, boost::python::list, boost::python::len etc.
dict wrapper_sum(list py_list) {
int length = len(py_list);
my_struct ms[length];
for(int i = 0; i < length; i++) {
dict py_dict = extract<dict>(py_list[i]);
ms[i].a = extract<int>(py_dict["a"]);
ms[i].b = extract<int>(py_dict["b"]);
}
dict py_res;
my_struct c_res = my_sum(ms, length);
py_res["a"] = c_res.a;
py_res["b"] = c_res.b;
return py_res;
}
BOOST_PYTHON_MODULE(my_wrapper){
def("my_sum", wrapper_sum);
}
my_python.py
import my_wrapper
data = [
{
"a": 3,
"b": 9
},
{
"a": 1,
"b": 3
},
{
"a": 6,
"b": 2
},
{
"a": 0,
"b": 4
}
]
res = my_wrapper.my_sum(data)
print(res)
I will do it:
$ make
$ python my_python.py
{'a': 10, 'b': 18}
With a little ingenuity, the function written in C for speed is I was very happy to be able to use it without rewriting it in Python.
Recommended Posts