问题描述
我有一个纯抽象类和2个继承类,其中每个类都包含一个称为go
的方法,如下所示:
// Pure abstract class
class Animal {
public:
virtual std::string go() = 0;
};
// First inherited class
class Dog : public Animal {
public:
std::string go() override { return "woof! "; }
};
// Second inherited class
class Cat : public Animal {
public:
std::string go() override { return "meow! "; }
};
在Python端,定义了实例化对象(Dog或Cat)的列表。在C ++方面,我试图编写一个函数call_go
,它将这个python列表作为输入,并调用列表中每个对象的方法go
。
import test
# defining a list of objects Cat or Dog
animals = []
animals.append(test.Cat())
animals.append(test.Dog())
animals.append(test.Cat())
# trying to call for each object of the given list the method "go"
test.call_go(animals)
由于我事先不知道给定列表中每个元素的类型,因此我尝试编写函数call_go
并将元素强制转换为Animal
:
void call_go(py::list animals) {
for (py::handle animal : animals) {
std::cout << py::cast<Animal>(animal).go() << " ";
}
}
但是,由于Animal
是一个抽象类,因此编译器返回:
错误:无效的抽象返回类型“动物”
但是,如果列表是在c ++代码中完全定义的,则它将编译并运行得很好:
void call_go_cpp() {
std::list<Animal*> animals;
animals.push_back(new Cat());
animals.push_back(new Dog());
animals.push_back(new Cat());
for(Animal* animal: animals)
std::cout << animal->go() << std::endl;
}
#include <iostream>
#include <string>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>
namespace py = pybind11;
// Pure abstract class
class Animal {
public:
virtual std::string go() = 0;
};
class PyAnimal : public Animal {
public:
using Animal::Animal;
std::string go() override { PYBIND11_OVERRIDE_PURE( std::string,Animal,go,); }
};
// First inherited class
class Dog : public Animal {
public:
std::string go() override { return "woof! "; }
};
class PyDog : public Dog {
public:
using Dog::Dog;
std::string go() override { PYBIND11_OVERRIDE(std::string,Dog,); }
};
// Second inherited class
class Cat : public Animal {
public:
std::string go() override { return "meow! "; }
};
class PyCat : public Cat {
public:
using Cat::Cat;
std::string go() override { PYBIND11_OVERRIDE(std::string,Cat,); }
};
// calling the method "go" for each element of a list (instance of class Dog or Cat) created within the c++ code
void call_go_cpp() {
std::list<Animal*> animals;
animals.push_back(new Cat());
animals.push_back(new Dog());
animals.push_back(new Cat());
for(Animal* animal: animals)
std::cout << animal->go() << std::endl;
}
// trying to call the method "go" for each element of a list (instance of class Dog or Cat) defined on the Python side
void call_go(py::list animals) {
for (py::handle animal : animals) {
std::cout << py::cast<Animal>(animal).go() << " ";
}
}
// Pybind11 bindings
PYBIND11_MODULE(test,m) {
py::class_<Animal,PyAnimal>(m,"Animal")
.def(py::init<>())
.def("go",&Animal::go);
py::class_<Dog,PyDog>(m,"Dog")
.def(py::init<>());
py::class_<Cat,PyCat>(m,"Cat")
.def(py::init<>());
m.def("call_go_cpp",&call_go_cpp);
m.def("call_go",&call_go);
}
解决方法
写call_go
的正确方法是
void call_go(py::list animals) {
for (py::handle animal : animals) {
// wrong
//std::cout << py::cast<Animal>(animal).go() << " ";
// correct way
std::cout << py::cast<Animal *>(animal)->go() << " ";
}
}
感谢https://gitter.im/pybind/Lobby的pybind11社区提供的解决方案