Pybind11 - 转换抽象继承 class 元素的列表
Pybind11 - Casting a list of abstract-inherited class elements
我有一个纯抽象 class 和 2 个继承的 classes,其中每个包含一个名为 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
是一个抽象 class,编译器 returns:
error: invalid abstract return type ‘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;
}
你知道如何解决这个问题吗?我想这涉及编写自定义演员表。
完整的 C++ 代码在这里:
#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, go, ); }
};
// 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, go, ); }
};
// 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, Animal, PyDog>(m, "Dog")
.def(py::init<>());
py::class_<Cat, Animal, 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 社区提供的解决方案
我有一个纯抽象 class 和 2 个继承的 classes,其中每个包含一个名为 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
是一个抽象 class,编译器 returns:
error: invalid abstract return type ‘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;
}
你知道如何解决这个问题吗?我想这涉及编写自定义演员表。
完整的 C++ 代码在这里:
#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, go, ); }
};
// 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, go, ); }
};
// 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, Animal, PyDog>(m, "Dog")
.def(py::init<>());
py::class_<Cat, Animal, 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 社区提供的解决方案