Reputation: 31
I would like to create a wrapper for a cpp class with Cython. But this class aggregates an other one and I don't know if i need to also create a wrapper for this class as I don't want to call this second class from Python.
There are the cpp classes:
testclass.hpp
#ifndef TESTCLASS_H
#define TESTCLASS_H
#include "ocean.hpp"
class TestClass {
private:
Ocean _ocean;
public:
int x, y;
TestClass();
~TestClass();
int Multiply(int a, int b);
};
#endif
testclass.cpp
#include "testclass.hpp"
TestClass::TestClass()
{
x = 5;
y = 1;
_ocean = Ocean();
}
TestClass::~TestClass()
{
std::cout << "Calling destructor" << std::endl;
}
int TestClass::Multiply(int a, int b)
{
return a*b;
}
ocean.hpp
#ifndef OCEAN_H
#define OCEAN_H
class Ocean {
public:
double _depth;
double _rho;
Ocean();
virtual ~Ocean();
void setwaterdepth(double d);
};
#endif
I would like to wrap the test class only, This is what I have tried:
ocean.pxd
cdef extern from "ocean.hpp":
cdef cppclass Ocean:
Ocean()
test.pyx
from ocean cimport Ocean
cdef extern from "testclass.hpp":
cdef cppclass TestClass:
TestClass()
int x
int y
int Multiply(int a, int b)
cdef class pyTestClass:
cdef TestClass* thisptr # hold a C++ instance
def __cinit__(self):
self.thisptr = new TestClass()
def __dealloc__(self):
del self.thisptr
def Multiply(self, a, b):
return self.thisptr.Multiply(a, b)
setup.py
from distutils.core import setup, Extension
from Cython.Build import cythonize
ext = Extension("test",
sources=["test.pyx", "testclass.cpp"],
language="c++")
setup(name="test",
ext_modules=cythonize(ext))
Q1) Is the right way of doing it ? (When I compile this I get the following error and I don't understand the error. )
C:\MinGW\bin\gcc.exe -mdll -O -Wall -IC:\Python27\include -IC:\Python27\PC -c testclass.cpp -o build\temp.win32-2.7\Release\testclass.o
writing build\temp.win32-2.7\Release\test.def
C:\MinGW\bin\g++.exe -shared -s build\temp.win32-2.7\Release\test.o build\temp.win32-2.7\Release\testclass.o build\temp.win32-2.7\Release\test.def -LC:\Python27\libs -LC:\Python27\PCbuild -LC:\Python27\PC\VS9.0 -lpython27 -lmsv
cr90 -o E:\00-Projets\InWave\Couplage\PYW\C++\test3\test.pyd
build\temp.win32-2.7\Release\testclass.o:testclass.cpp:(.text+0x97): undefined reference to `Ocean::~Ocean()'
build\temp.win32-2.7\Release\testclass.o:testclass.cpp:(.text+0xa3): undefined reference to `Ocean::~Ocean()'
build\temp.win32-2.7\Release\testclass.o:testclass.cpp:(.text+0xe4): undefined reference to `Ocean::Ocean()'
build\temp.win32-2.7\Release\testclass.o:testclass.cpp:(.text+0xfa): undefined reference to `Ocean::Ocean()'
build\temp.win32-2.7\Release\testclass.o:testclass.cpp:(.text+0x11a): undefined reference to `Ocean::~Ocean()'
build\temp.win32-2.7\Release\testclass.o:testclass.cpp:(.text+0x196): undefined reference to `Ocean::~Ocean()'
collect2.exe: error: ld returned 1 exit status
error: command 'C:\\MinGW\\bin\\g++.exe' failed with exit status 1
Upvotes: 1
Views: 281
Reputation: 31
I finally manage to make it work.
There is no need to wrap the Ocean class and consequently to create a .pxd
file for it.
But in setup.py
it is important to include all .cpp
dependancies.
As said in previous comments it is also important to add include guard in headers file.
So here is a working code (compiled with python setup.py build_ext --inplace
)
ocean.hpp
#ifndef OCEAN_H
#define OCEAN_H
class Ocean {
public:
double _depth;
double _rho;
Ocean();
virtual ~Ocean();
void setwaterdepth(double d);
};
#endif
testclass.hpp
#ifndef TESTCLASS_H
#define TESTCLASS_H
#include "ocean.hpp"
class TestClass {
private:
Ocean _ocean;
public:
int x, y;
TestClass();
virtual ~TestClass();
int Multiply(int a, int b);
void _set_x(int x);
};
#endif
testclass.cpp
#include <iostream>
#include "testclass.hpp"
#include "ocean.hpp"
TestClass::TestClass()
{
x = 5;
y = 1;
_ocean = Ocean();
std::cout << "Calling constructor" << std::endl;
}
TestClass::~TestClass()
{
std::cout << "Calling destructor" << std::endl;
}
int TestClass::Multiply(int a, int b)
{
return a*b;
}
void TestClass::_set_x(int new_x)
{
x = new_x;
}
test.pyx
cdef extern from "testclass.hpp":
cdef cppclass TestClass:
TestClass()
int x
int y
int Multiply(int a, int b)
cdef class pyTestClass:
cdef TestClass* thisptr # hold a C++ instance
def __cinit__(self):
self.thisptr = new TestClass()
def __dealloc__(self):
del self.thisptr
def Multiply(self, a, b):
return self.thisptr.Multiply(a, b)
property y:
# Here we use a property to expose the public member
# y of TestClass to Python
def __get__(pyTestClass self):
return self.thisptr.y
def __set__(pyTestClass self, value):
self.thisptr.y = <int> value
setup.py
from distutils.core import setup, Extension
from Cython.Build import cythonize
ext = Extension("test",
sources=["test.pyx", "testclass.cpp", "ocean.cpp"],
language="c++")
setup(name="test", ext_modules=cythonize(ext))
test of the wrapping
import test as wrapper
T = wrapper.pyTestClass()
print T.Multiply(3, 5)
print T.y
T.y = 3
print T.y
outputs
Calling ocean constructor
Calling ocean constructor
Calling ocean destructor
Calling constructor
15
1
3
Calling destructor
Calling ocean destructor
Upvotes: 2