jeudi 25 août 2022

Cython: error: no matching function for call to …

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.

Aucun commentaire:

Enregistrer un commentaire