Search code examples
pythonc++numpyarmadillopybind11

Error by wrapping c++ class with pybind11 using carma (armadillo matrices and numpy arrays)


I have a simple example of trying to use carma and pybind11 to go to and from armadillo matrices and numpy arrays based off of this example. Without the printarma() function, the code works i.e. with just wrapping using pybind11 the code compiles and I can run it in python. The problem here seems to be with carma. Here is the code:

#include <ostream>
#include <iostream>
#include <armadillo>
#include <carma/carma.h>
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>

namespace py = pybind11;

using namespace std;

class Base
{
public:
    virtual void test() = 0;
    virtual void printarma() = 0;
};
class Derived: public Base
{

public:
    void printarma(py::array_t<double> & arr) {arma::Mat<double> mat = carma::arr_to_mat<double>(arr);
            std::cout << mat<< std::endl;}
    void test() {cout << "Test";}
};


PYBIND11_MODULE(example,m) {
    py::class_<Base>(m, "Base");

    py::class_<Derived, Base>(m, "Derived")
        .def(py::init<>())
        .def("test", &Derived::test);
        m.def("printarma", &Derived::printarma,py::arg("arr"));
} 

I run the command:

c++ -O3 -Wall -shared -std=c++14 -fPIC -larmadillo `python3 -m pybind11 --includes` abstrakt_test.cpp -o example`python3-config --extension-suffix`

And the errors are:

In file included from /home/mikanim/anaconda3/envs/pybind/lib/python3.7/site-packages/pybind11/include/pybind11/pybind11.h:47,
                 from /home/mikanim/anaconda3/envs/pybind/lib/python3.7/site-packages/pybind11/include/pybind11/numpy.h:12,
                 from /usr/local/include/carma/carma/converters.h:24,
                 from /usr/local/include/carma/carma/arraystore.h:1,
                 from /usr/local/include/carma/carma.h:1,
                 from abstrakt_test.cpp:18:
/home/mikanim/anaconda3/envs/pybind/lib/python3.7/site-packages/pybind11/include/pybind11/detail/init.h: In instantiation of ‘Class* pybind11::detail::initimpl::construct_or_initialize(Args&& ...) [with Class = Derived; Args = {}; typename std::enable_if<(! std::is_constructible<_Tp, _Args>::value), int>::type <anonymous> = 0]’:
/home/mikanim/anaconda3/envs/pybind/lib/python3.7/site-packages/pybind11/include/pybind11/detail/init.h:174:66:   required from ‘static void pybind11::detail::initimpl::constructor<Args>::execute(Class&, const Extra& ...) [with Class = pybind11::class_<Derived, Base>; Extra = {}; typename std::enable_if<(! Class::has_alias), int>::type <anonymous> = 0; Args = {}]’
/home/mikanim/anaconda3/envs/pybind/lib/python3.7/site-packages/pybind11/include/pybind11/pybind11.h:1148:9:   required from ‘pybind11::class_<type_, options>& pybind11::class_<type_, options>::def(const pybind11::detail::initimpl::constructor<Args ...>&, const Extra& ...) [with Args = {}; Extra = {}; type_ = Derived; options = {Base}]’
abstrakt_test.cpp:45:26:   required from here
/home/mikanim/anaconda3/envs/pybind/lib/python3.7/site-packages/pybind11/include/pybind11/detail/init.h:63:64: error: invalid new-expression of abstract class type ‘Derived’
 inline Class *construct_or_initialize(Args &&...args) { return new Class{std::forward<Args>(args)...}; }
                                                                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
abstrakt_test.cpp:31:7: note:   because the following virtual functions are pure within ‘Derived’:
 class Derived: public Base
       ^~~~~~~
abstrakt_test.cpp:29:18: note:  ‘virtual void Base::printarma()’
     virtual void printarma() = 0;
                  ^~~~~~~~~
In file included from /home/mikanim/anaconda3/envs/pybind/lib/python3.7/site-packages/pybind11/include/pybind11/numpy.h:12,
                 from /usr/local/include/carma/carma/converters.h:24,
                 from /usr/local/include/carma/carma/arraystore.h:1,
                 from /usr/local/include/carma/carma.h:1,
                 from abstrakt_test.cpp:18:
/home/mikanim/anaconda3/envs/pybind/lib/python3.7/site-packages/pybind11/include/pybind11/pybind11.h: In instantiation of ‘void pybind11::cpp_function::initialize(Func&&, Return (*)(Args ...), const Extra& ...) [with Func = pybind11::cpp_function::cpp_function(Return (Class::*)(Arg ...), const Extra& ...) [with Return = void; Class = Derived; Arg = {pybind11::array_t<double, 16>&}; Extra = {pybind11::name, pybind11::scope, pybind11::sibling, pybind11::arg}]::<lambda(Derived*, pybind11::array_t<double>&)>; Return = void; Args = {Derived*, pybind11::array_t<double, 16>&}; Extra = {pybind11::name, pybind11::scope, pybind11::sibling, pybind11::arg}]’:
/home/mikanim/anaconda3/envs/pybind/lib/python3.7/site-packages/pybind11/include/pybind11/pybind11.h:78:9:   required from ‘pybind11::cpp_function::cpp_function(Return (Class::*)(Arg ...), const Extra& ...) [with Return = void; Class = Derived; Arg = {pybind11::array_t<double, 16>&}; Extra = {pybind11::name, pybind11::scope, pybind11::sibling, pybind11::arg}]’
/home/mikanim/anaconda3/envs/pybind/lib/python3.7/site-packages/pybind11/include/pybind11/pybind11.h:819:22:   required from ‘pybind11::module& pybind11::module::def(const char*, Func&&, const Extra& ...) [with Func = void (Derived::*)(pybind11::array_t<double>&); Extra = {pybind11::arg}]’
abstrakt_test.cpp:47:55:   required from here
/home/mikanim/anaconda3/envs/pybind/lib/python3.7/site-packages/pybind11/include/pybind11/pybind11.h:133:50: error: static assertion failed: The number of argument annotations does not match the number of function arguments
         static_assert(expected_num_args<Extra...>(sizeof...(Args), cast_in::has_args, cast_in::has_kwargs),
                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /home/mikanim/anaconda3/envs/pybind/lib/python3.7/site-packages/pybind11/include/pybind11/attr.h:13,
                 from /home/mikanim/anaconda3/envs/pybind/lib/python3.7/site-packages/pybind11/include/pybind11/pybind11.h:44,
                 from /home/mikanim/anaconda3/envs/pybind/lib/python3.7/site-packages/pybind11/include/pybind11/numpy.h:12,
                 from /usr/local/include/carma/carma/converters.h:24,
                 from /usr/local/include/carma/carma/arraystore.h:1,
                 from /usr/local/include/carma/carma.h:1,
                 from abstrakt_test.cpp:18:
/home/mikanim/anaconda3/envs/pybind/lib/python3.7/site-packages/pybind11/include/pybind11/cast.h:1958:57: error: ‘std::enable_if_t<std::is_void<_Res>::value, pybind11::detail::void_type> pybind11::detail::argument_loader<Args>::call(Func&&) && [with Return = void; Guard = pybind11::detail::void_type; Func = pybind11::cpp_function::cpp_function(Return (Class::*)(Arg ...), const Extra& ...) [with Return = void; Class = Derived; Arg = {pybind11::array_t<double, 16>&}; Extra = {pybind11::name, pybind11::scope, pybind11::sibling, pybind11::arg}]::<lambda(Derived*, pybind11::array_t<double>&)>&; Args = {Derived*, pybind11::array_t<double, 16>&}; std::enable_if_t<std::is_void<_Res>::value, pybind11::detail::void_type> = pybind11::detail::void_type]’, declared using local type ‘pybind11::cpp_function::cpp_function(Return (Class::*)(Arg ...), const Extra& ...) [with Return = void; Class = Derived; Arg = {pybind11::array_t<double, 16>&}; Extra = {pybind11::name, pybind11::scope, pybind11::sibling, pybind11::arg}]::<lambda(Derived*, pybind11::array_t<double>&)>’, is used but never defined [-fpermissive]
     enable_if_t<std::is_void<Return>::value, void_type> call(Func &&f) && {

Thanks in advance!

EDIT:

Code without carma:

c++ -O3 -Wall -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` abstrakt_test.cpp -o example`python3-config --extension-suffix`



#include <ostream>
#include <iostream>
#include <pybind11/pybind11.h>


namespace py = pybind11;
using namespace std;

class Base
{
public:
    virtual void test() = 0;
};
class Derived: public Base
{

public:

    void test() {cout << "Test";}
};

PYBIND11_MODULE(example,m) {
    py::class_<Base>(m, "Base");

    py::class_<Derived, Base>(m, "Derived")
        .def(py::init<>())
        .def("test", &Derived::test);
} 

Solution

  • At the end it was two things: the virtual function in the Base class needed to add py::array_t<double> & arr to it and the semicolon at the end at .def needed to be deleted along with the m before .def. This code compiles and works in python:

    class Base
    {
    public:
        virtual void test() = 0;
        virtual void printarma(py::array_t<double> & arr) = 0;
    };
    class Derived: public Base
    {
    
    public:
        void printarma(py::array_t<double> & arr) {arma::Mat<double> mat = carma::arr_to_mat<double>(arr);
                std::cout << mat<< std::endl;}
        void test()  {cout << "Test";}
    };
    
    
    PYBIND11_MODULE(example,m) {
        py::class_<Base>(m, "Base");
    
        py::class_<Derived, Base>(m, "Derived")
            .def(py::init<>())
            .def("test", &Derived::test)
            .def("printarma", &Derived::printarma,py::arg("arr"));