I'm taking my first steps in Cython to write a Python extension of some C code. Also, I have only superficial knowledge of C . Now I face an error which I do not understand, when cythonizing my .pyx
file. The C 11 code is generated successfully, but that code does not compile. The first lines of the compiler error message are:
$ python setup.py build_ext --inplace
Compiling py_foo.pyx because it changed.
[1/1] Cythonizing py_foo.pyx
running build_ext
building 'pyfoo' extension
creating build
creating build/temp.linux-x86_64-3.9
gcc -pthread -B /home/…/compiler_compat -Wno-unused-result -Wsign-compare -DNDEBUG -O2 -Wall -fPIC -O2 -isystem /home/…/include -I/home/…/include -fPIC -O2 -isystem /home/…/include -fPIC -I. -I./ -I/home/…/include/python3.9 -c ./example.cpp -o build/temp.linux-x86_64-3.9/./example.o -std=c 11
gcc -pthread -B /home/…/compiler_compat -Wno-unused-result -Wsign-compare -DNDEBUG -O2 -Wall -fPIC -O2 -isystem /home/…/include -I/home/…/include -fPIC -O2 -isystem /home/…/include -fPIC -I. -I./ -I/home/…/include/python3.9 -c ./foo.cpp -o build/temp.linux-x86_64-3.9/./foo.o -std=c 11
gcc -pthread -B /home/…/compiler_compat -Wno-unused-result -Wsign-compare -DNDEBUG -O2 -Wall -fPIC -O2 -isystem /home/…/include -I/home/…/include -fPIC -O2 -isystem /home/…/include -fPIC -I. -I./ -I/home/…/include/python3.9 -c py_foo.cpp -o build/temp.linux-x86_64-3.9/py_foo.o -std=c 11
py_foo.cpp: In function ‘void __pyx_pf_5pyfoo_9PyDerived_2__dealloc__(__pyx_obj_5pyfoo_PyDerived*)’:
py_foo.cpp:1913:24: warning: deleting object of polymorphic class type ‘nspace::Derived’ which has non-virtual destructor might cause undefined behavior [-Wdelete-non-virtual-dtor]
1913 | delete __pyx_v_self->c_derived;
| ^~~~~~~~~
py_foo.cpp: In function ‘int __pyx_pf_5pyfoo_5PyFoo___cinit__(__pyx_obj_5pyfoo_PyFoo*, __pyx_obj_5pyfoo_PyBase*)’:
py_foo.cpp:2134:66: error: no matching function for call to ‘std::unique_ptr<nspace::Base>::unique_ptr(PyObject*&)’
2134 | __pyx_t_2 = new nspace::Foo(((std::unique_ptr<nspace::Base> )__pyx_t_1));
| ^~~~~~~~~
In file included from /usr/include/c /9/memory:80,
from py_foo.cpp:729:
/usr/include/c /9/bits/unique_ptr.h:281:2: note: candidate: ‘template<class _Up, class> std::unique_ptr<_Tp, _Dp>::unique_ptr(std::auto_ptr<_Up>&&)’
281 | unique_ptr(auto_ptr<_Up>&& __u) noexcept;
| ^~~~~~~~~~
/usr/include/c /9/bits/unique_ptr.h:281:2: note: template argument deduction/substitution failed:
py_foo.cpp:2134:66: note: mismatched types ‘std::auto_ptr<_Up>’ and ‘PyObject*’ {aka ‘_object*’}
2134 | __pyx_t_2 = new nspace::Foo(((std::unique_ptr<nspace::Base> )__pyx_t_1));
|
...
These are the relevant files of my MWE:
example.h
:
#ifndef EXAMPLE_H
#define EXAMPLE_H
namespace nspace
{
class Base
{
public:
virtual void print_() const = 0;
};
class Derived : public Base
{
private:
int i;
public:
Derived(int i);
void print_() const;
};
}
#endif /* EXAMPLE_H */
example.cpp
:
#include <iostream>
#include "example.h"
namespace nspace
{
Derived::Derived(int i)
{
this->i = i;
}
void Derived::print_() const
{
std::cout << this->i << std::endl;
}
}
foo.h
:
#ifndef FOO_H
#define FOO_H
#include <memory>
#include "example.h"
namespace nspace
{
class Foo
{
public:
Foo();
Foo(std::unique_ptr<Base> base);
void print_();
private:
std::unique_ptr<Base> base;
};
}
#endif // FOO_H
foo.cpp
:
#include "foo.h"
#include "example.h"
namespace nspace
{
Foo::Foo()
{
this->base = std::unique_ptr<Base>(new Derived(0));
}
Foo::Foo(std::unique_ptr<Base> base)
{
this->base = std::move(base);
}
void Foo::print_()
{
this->base->print_();
}
}
cpp_foo.pxd
:
from libcpp.memory cimport unique_ptr
cdef extern from "foo.h" namespace "nspace":
cdef cppclass Foo:
Foo() except
Foo(unique_ptr[Base] cpp_base) except
print_()
cdef extern from "example.h" namespace "nspace":
cdef cppclass Base:
pass
cdef cppclass Derived(Base):
Derived(int i) except
py_foo.pyx
:
# distutils: language = c
# distutils: extra_compile_args = -std=c 11
cimport cython
from cpp_foo cimport Foo, Base, Derived
from libcpp.memory cimport unique_ptr
cdef class PyBase:
pass
cdef class PyDerived(PyBase):
cdef Derived* c_derived
def __cinit__(self, int i):
self.c_derived = new Derived(i)
def __dealloc__(self):
del self.c_derived
cdef class PyFoo:
cdef Foo* foo
def __cinit__(self, PyBase py_base):
self.foo = new Foo(
cpp_base=<unique_ptr[Base]>(py_base.c_derived)
)
def __dealloc__(self):
del self.foo
def print_(self):
self.foo.print_()
setup.py
:
from distutils.core import setup, Extension
from Cython.Build import cythonize
extensions = [
Extension("pyfoo",
sources=[
"py_foo.pyx",
"./foo.cpp",
"./example.cpp",
],
include_dirs=["./"]
)
]
setup(
ext_modules=cythonize(
extensions,
language_level=3,
),
)
I'm very sure my error comes from a misconception I have regarding Cython (and maybe C too). Any help or tip is appreciated.
CodePudding user response:
There's a few problems. I've listed three that I've spotted quickly but there may well be more.
The cast
I think you want to call unique_ptr rather than cast to it
cpp_base=unique_ptr[Base](py_base.c_derived)
The <>
cast generates a C-style cast which the C compiler doesn't know what to do with.
py_base
doesn't have an attribute c_derived
Therefore this'll default to assuming that c_derived
is a Python attribute returning a Python object.
PyDerived
has the attribute c_derived
. If you want to access this attribute then the argument to PyFoo.__cinit__
needs to be of type PyDerived
.
Ownership
By wrapping py_base.c_derived
with a unique_ptr
you're passing ownership to the unique_ptr
. Despite this, py_base
still believes it owns this pointer and will delete it in __dealloc__
.