Is there any place I can find a working example to wrap a C++ function (using VTK) to be used by
Python?
Searched and found this page
mix VTK and SWIG Python
but there is no compiling details, and this simple example on this page
https://public.kitware.com/pipermail/vtkusers/2002-October/013980.html
But for my system Ubuntu 16.04, vtk 8.2.0, Python 3.6.8, I could not make the example work.
I have to modify them to let the compiling done without error.
Details:
#============simple.cpp===================
// simple.c
//based on https://public.kitware.com/pipermail/vtkusers/2002-October/013980.html
#include "Python.h"
#include "vtkPythonUtil.h"
#include "vtkObject.h"
#include "vtkObjectBase.h"
#include <stdio.h>
#include "vtkRenderer.h"
extern "C"
{
static PyObject* simple_vtk_function(PyObject *self, PyObject *args);
}
static PyObject* simple_vtk_function(PyObject *self, PyObject *args)
{
PyObject *vtkpy;
if (!PyArg_ParseTuple(args, "O", &vtkpy))
return NULL;
vtkObjectBase *obj = vtkPythonUtil::GetPointerFromObject(vtkpy,"PyObject");
/* Do what you want to do here. */
//printf("%s\n", obj->GetClassName());
printf("inside module simple_vtk, function simple_vtk_function\n");
Py_RETURN_NONE;
}
static char simple_docs[] =
"simple( ): Any message you want to put here!!\n";
static PyMethodDef module_methods[] =
{
{
"simple_vtk_function", (PyCFunction) simple_vtk_function,
METH_VARARGS, simple_docs
},
{NULL}
};
static struct PyModuleDef simple_vtk =
{
PyModuleDef_HEAD_INIT,
"simple_vtk",/* name of module */
"",/* module documentation, may be NULL */
-1,
module_methods
};
PyMODINIT_FUNC PyInit_simple_vtk(void)
{
return PyModule_Create(&simple_vtk);
}
#============setup.py===================
#!/usr/bin/env python3
import distutils.core as dc
module = dc.Extension('simple_vtk',
sources = ['simple.cpp'],
include_dirs=['/usr/local/include/vtk-8.2'],
libraries_dirs = ['/usr/local/include/vtk-8.2'],
#libraries=['vtkPythonUtil']
)
dc.setup(name = 'simple_vtk',
version = '1.0',
ext_modules = [module])
compile command
CFLAGS="-std=c++11" python setup.py build
and get this printing out
/home/jun/anaconda3/lib/python3.6/distutils/extension.py:131: UserWarning: Unknown Extension options: 'libraries_dirs'
warnings.warn(msg)
running build
running build_ext
building 'simple_vtk' extension
gcc -pthread -B /home/jun/anaconda3/compiler_compat -Wl,--sysroot=/ -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -std=c++11 -fPIC -I/usr/local/include/vtk-8.2 -I/home/jun/anaconda3/include/python3.6m -c simple.cpp -o build/temp.linux-x86_64-3.6/simple.o
cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++
simple.cpp: In function ‘PyObject* simple_vtk_function(PyObject*, PyObject*)’:
simple.cpp:22:19: warning: unused variable ‘obj’ [-Wunused-variable]
vtkObjectBase *obj = vtkPythonUtil::GetPointerFromObject(vtkpy,"PyObject");
^
g++ -pthread -shared -B /home/jun/anaconda3/compiler_compat -L/home/jun/anaconda3/lib -Wl,-rpath=/home/jun/anaconda3/lib -Wl,--no-as-needed -Wl,--sysroot=/ -std=c++11 build/temp.linux-x86_64-3.6/simple.o -o build/lib.linux-x86_64-3.6/simple_vtk.cpython-36m-x86_64-linux-gnu.so
to test in python
>>> import simple_vtk
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: /home/jun/project/play/swig/for_vtk_v2/simple_vtk.cpython-36m-x86_64-linux-gnu.so: undefined symbol: _ZN13vtkPythonUtil20GetPointerFromObjectEP7_objectPKc
The C++ function is so simple, it just to convert a PyObject and convert to vtkObjectBase. I didn't even remove the simple print
printf("%s\n", obj->GetClassName());
But I could never make it work. I think it is a common need for people to wrap a custom C++ vtk function for Python, a little bit surpring there is almost no woring example on internet.
Could you please help me on this? Thanks!
Related
i am trying to wrap the C++ library PyrochloreAFM.hpp which itself uses the lib boost/randoom.hpp so that i can import it as a python module. The C++ part itself works fine and i can succesfully import and use all of this from my main.
#include "PyrochloreAFM.hpp"
int main (int argc, const char *argv[]) {
PyrochloreAFM pyrochloreAFM (¶meters, &statistics, &rng);
}
Now following a tutorial i set up my c++ wrapper:
// pybind11_wrapper.cpp
#include <pybind11/pybind11.h>
#include <PyrochloreAFM.hpp>
PYBIND11_MODULE(pybind11_example, m) {
m.doc() = "pybind11 example plugin"; // Optional module docstring
m.def("cpp_function", &PyrochloreAFM, "A function that multiplies two numbers");
}
and my tasks.py file
# tasks.py
import invoke
invoke.run(
"g++ -O3 -Wall -Werror -shared -std=c++11 -fPIC PyrochloreAFM.cpp "
"-o libpyro.so "
)
However now $invoke build-PyrochloreAFM or even $invoke --list seem to have lost the track of the standard C++ library.
In file included from PyrochloreAFM.cpp:1:
./Parameters.hpp:16:10: fatal error: 'boost/random.hpp' file not found
#include "boost/random.hpp" // tested with boost 1.53
^~~~~~~~~~~~~~~~~~
1 error generated.
This might be a simple PATH problem so i would be very glad for any tips!
Thank you, Andres!
"Of course" the tasks.py file which compiles the .cpp file through the python module invoke has to instruct the compiler g++ to use the libraries we desire. In my case it is the flag -I /opt/homebrew/Cellar/boost/1.80.0/include i also use in my makefile or directly in terminal.
I found this very helpful question and answer:
Return vector<pair<int,int>> & from c++ method to python list of tuples using swig typemap
However I have some problems iterating the return vector if it's not a reference, here the example:
myclass.h:
#include <vector>
#include <utility>
using std::vector;
using std::pair;
class MyClass {
private:
vector<pair<int,int> > _myvector;
public:
MyClass( );
const vector<pair<int,int> > & GetMyVector() const;
};
myclass.cpp:
#include "myclass.h"
MyClass::MyClass(): _myvector()
{_myvector.push_back(std::make_pair(1,2));_myvector.push_back(std::make_pair(1,2));};
const vector<pair<int,int>> & MyClass::GetMyVector() const {
return _myvector;
};
myclass.i:
%module x
%include <std_pair.i>
%include <std_vector.i>
%include <std_string.i>
%template() std::pair<int,int>;
%template(PairVector) std::vector<std::pair<int,int> >;
%{
#include "myclass.h"
%}
%include "myclass.h"
compiled with:
g++ -std=c++11 -c -fPIC myclass.cpp
swig -c++ -v -python myclass.i
g++ -std=c++11 -fPIC -c myclass.cpp myclass_wrap.cxx -I/usr/include/python2.7
g++ myclass.o myclass_wrap.o -shared -fPIC -o _x.so
but when I run something like this in python:
import x
b=x.MyClass()
print(b.GetMyVector())
for a,b in b.GetMyVector():
print(a,b)
then I get:
<Swig Object of type 'vector< std::pair< int,int >,std::allocator< std::pair< int,int > > > *' at 0x7ff06804b1b0>
Traceback (most recent call last):
File "Test.py", line 6, in <module>
for a,b in b.GetMyVector():
TypeError: 'SwigPyObject' object is not iterable
How can I iterate the returned vector properly in python? And why is a pointer to vector returned? Do I have to change something in the swig file?
In case it is relevant: (on Ubuntu)
SWIG Version 2.0.11
g++ (Ubuntu 4.9.4-2ubuntu1~14.04.1) 4.9.4
Python 2.7.6
SWIG doesn't understand the using directive correctly.
Same as in this question and answer:
SWIG argument error when using "using std::vector" in python
As to why a pointer is returned, well, if SWIG cannot convert a returned object into a python object, then it wraps a pointer to the object.
This question already has an answer here:
Build Python (2.7) module on GCC 4.8 fails
(1 answer)
Closed 7 years ago.
I am currently working on building a python module in C. I have followed a tutorial series to get my self to a building point, and I have a pretty good grasp of what is going on.
However, when I run the script to set everything up I have the following printout. Looks like the error is coming from one of the header files in python-dev; however, I wanted to reach out here to make sure it isn't something I am doing. I am going to include the error code, and I can provide the other files on request.
sudo python setup.py build
running build
running build_ext
building 'lidar' extension
arm-linux-gnueabihf-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security -fPIC -I/usr/local/include -I/usr/include/python2.7 -c lidar_module.c -o build/temp.linux-armv6l-2.7/lidar_module.o
lidar_module.c: In function ‘lidar_test’:
lidar_module.c:8:6: warning: unused variable ‘sts’ [-Wunused-variable]
int sts = 0;
^
In file included from /usr/include/python2.7/Python.h:58:0,
from lidar_module.c:1:
lidar_module.c: At top level:
/usr/include/python2.7/pyport.h:802:39: error: expected ‘,’ or ‘;’ before ‘void’
# define PyMODINIT_FUNC void
^
lidar_module.c:24:1: note: in expansion of macro ‘PyMODINIT_FUNC’
PyMODINIT_FUNC initlidar(){
^
lidar_module.c:3:18: warning: ‘lidarError’ defined but not used [-Wunused-variable]
static PyObject *lidarError;
^
lidar_module.c:19:20: warning: ‘lidar_methods’ defined but not used [-Wunused-variable]
static PyMethodDef lidar_methods[] = {
^
error: command 'arm-linux-gnueabihf-gcc' failed with exit status 1
I peeked into the header file and found this around the area of the error.
#ifndef PyMODINIT_FUNC
# if defined(__cplusplus)
# fndef PyMODINIT_FUNC
# if defined(__cplusplus)
# define PyMODINIT_FUNC extern "C" void
# else /* __cplusplus */
# define PyMODINIT_FUNC void
# endif /* __cplusplus */
#endif
I literally had to search the line number output from the console, but I did find the solution here:
Build Python (2.7) module on GCC 4.8 fails
I'm trying to create a *.so file for further use in Python using SWIG, but something isn't working.
I have two files:
DataGatherer.h
#include <iostream>
#include <stdlib.h>
#include <time.h>
#include "gnublin.h"
#include <pthread.h>
class dataGatherer
{
private:
int threshold;
int timeThreshold;
int data[4096];
bool running;
gnublin_spi* spiDevice;
pthread_t spiThread;
void *params;
public:
dataGatherer(void);
dataGatherer(int, int);
void initData();
int getThreshold(void);
int* getData(void);
int getPeak(void);
void initSPI(void);
void gatherData();
void * run(void * arg);
void stop(void);
// for use of thread we have to implement some methods from C
static void * start_static(void * params)
{
dataGatherer * thread_this = static_cast<dataGatherer*>(params);
return thread_this->run(thread_this->params);
}
void start(void * params)
{
this->params = params;
pthread_create(&spiThread, 0, &dataGatherer::start_static, this);
}
};
and spiController.h
#include "dataGatherer.h"
class spiController
{
private:
bool runGather;
dataGatherer* gatherer;
int data[4096];
public:
spiController(void);
spiController(int, int);
void initData();
bool checkStop();
void stop();
void start();
};
My spiController.i interface file looks like this:
/* spiController.i */
%module spiController
%{
#include "dataGatherer.h"
#include "spiController.h"
#include "gnublin.h"
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
extern void initData();
extern bool checkStop();
extern void stop();
extern void start();
%}
extern void initData();
extern bool checkStop();
extern void stop();
extern void start();
At the end I try to create the *.so file using commands in the terminal like in the example on the SWIG page with:
swig -python -c++ spiController.i
c++ -c spiController_wrap.c -I/usr/include/python2.7
c++ -shared spiController_wrap.o -o _spiController.so
*.cxx, *.o and *.so file are created with no error, but when I import the spiController into the python code I get:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "spiController.py", line 26, in <module>
_spiController = swig_import_helper()
File "spiController.py", line 22, in swig_import_helper
_mod = imp.load_module('_spiController', fp, pathname, description)
ImportError: ./_spiController.so: undefined symbol: _Z9checkStopv
It's my first try using SWIG and I'm already stuck at this point. How can I resolve this?
I just got the same error and finally figured out why. As above people said, when it says unfound symbol like yours and gives the undefined function name '_Z9checkStopv', always check for the implementation of this function in .cpp file as well as any function declaration of the same name!!
For my case, my cpp does define my 'unfound symbol' constructor function, but in my .h file, i have an overloaded operator= (for the constructor) which is undefined in .cpp file. So swig wraps both default constructor(implemented in .cpp) and operator= (not implemented). Therefore when import, this unimplemented operator= produces the error. Hope this helps!
You must link the library that defines your C++ functions that you have declared like checkStop etc. You would add -L<path to your C++ DLL> -l<name of your C++ DLL> on 3rd line of your example's compile steps.
Like this:
c++ -L<path to DLL> -l<name of your dll> -shared spiController_wrap.o -o _spiController.so
Just as Adam's comment and my experience, you should first compile your XXX.cpp file into XXX.o, the whole command lines maybe like following:
swig -python -c++ XXX.i
g++ -c -fpic XXX.cpp* (this command will generate XXX.o file)
g++ -c -fpic XXX_wrap.cxx -I/usr/include/python2.7* (this command will generate XXX_wrap.o file)
g++ -shared XXX.o XXX_wrap.o -o XXX.so
Although there may be many causes for this problem, I got the exact same error when I compiled the shared library with the python v3.5 headers, e.g.
swig -python example.i
gcc -fPIC -c example.c example_wrap.c -I/usr/include/python3.5 # <-- ISSUE HERE
gcc -shared example.o example_wrap.o -o _example.so
But then later tried to use the example library using python test.py, which ran python v2.7 on system (so it was a python version mismatch issue).
In my case I was also getting that error, and spent some time trying out stuff without success. My problem was that although my source file was plain C I had it named with the .cpp extension, assuming it wouldn't matter. Changing the extension to .c solved automatically the issue.
Another way of solving it was to add the line #include "example.cpp" to the header section of SWIG's .i file.
So, summarizing:
example.c
int fact(int n) {
if (n <= 1) return 1;
else return n*fact(n-1);
}
example.i
%module example
%{
extern int fact(int n);
%}
extern int fact(int n);
Then the following worked for me (in Ubuntu 17.10):
swig -c++ -python example.i
gcc -fPIC -c example.c example_wrap.c -I /usr/include/python2.7
gcc -shared example.o example_wrap.o -o _example.so
python -c "import example; print example.fact(5)"
Hope this helps someone!
Cheers
I know the solution
At the end of make up the share,you should usr g++ -shared -fpic *.o *.o -o _***.so
I have a C file dtls_udp_echo.c in which I use SSL functions. I am trying to create a Python wrapper for this file using SWIG. I've done the following steps:
1) Created an interface file udp.i:
%module udp
%{
/* Put header files here or function declarations like below */
#define SWIG_FILE_WITH_INIT
#include "dtls_udp_echo.h"
%}
int THREAD_setup();
int THREAD_cleanup();
int handle_socket_error();
int generate_cookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len);
int verify_cookie(SSL *ssl, unsigned char *cookie, unsigned int cookie_len);
int dtls_verify_callback (int ok, X509_STORE_CTX *ctx) ;
void* connection_handle(void *info);
void start_server(int port, char *local_address);
void start_client(char *remote_address, char *local_address, int port, int length, int messagenumber);
2) Run the command swig -python udp.i.
3) Run the command gcc -O2 -fPIC -c dtls_udp_echo.c -I/usr/local/ssl/include -L/usr/local/ssl/lib -lcrypto -lssl. The path to the include and library is correct, I checked it!
4) Run the command gcc -O2 -fPIC -c udp_wrap.c -I/usr/include/python2.5 -I/usr/local/ssl/include -L/usr/local/ssl/lib -lcrypto -lssl.
5) Run the command gcc -shared dtls_udp_echo.o udp_wrap.o -o _udp.so.
It seems to complete OK as no errors are reported. But, when I try to import the module, I get the following traceback:
>>> import udp
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "udp.py", line 28, in <module>
> import _udp ImportError: ./_udp.so: undefined symbol:
> SSL_get_rbio
Can anybody help me to fix this problem?
It can't find the OpenSSL library. Add it to your ld search path; see the ldconfig(8) man page for details.