pybind11
pybind11 copied to clipboard
[BUG]: send a python function to c++ as a std::function to create Python subclass instance,the constructor of python class be called but the instance still the base class
Required prerequisites
- [X] Make sure you've read the documentation. Your issue may be addressed there.
- [x] Search the issue tracker and Discussions to verify that this hasn't already been reported. +1 or comment there if it has.
- [x] Consider asking first in the Gitter chat room or in a Discussion.
What version (or hash if on master) of pybind11 are you using?
2.10.4
Problem description
- my cpp code
class Pet {
public:
Pet()
{
id = makePetId();
printf("Pet::Pet\n");
}
virtual ~Pet() { printf("Pet::~Pet\n"); }
Pet(const Pet&) = delete;
Pet(const Pet&&) = delete;
virtual std::string name() const { return "pet"; }
static int makePetId()
{
static int id = 0;
return id++;
}
int id = -1;
};
class PyPet : public Pet {
public:
using Pet::Pet;
std::string name() const override { PYBIND11_OVERRIDE(std::string, Pet, name, ); }
};
class PetRoom {
public:
PetRoom() = default;
void echoPetName(std::shared_ptr<Pet> pet) { printf("pet name: %s\n", pet->name().c_str()); }
void addPet(std::shared_ptr<Pet> pet)
{
printf("add pet: %s\n", pet->name().c_str());
pets.push_back(pet);
}
void addPetCustom(std::function<std::shared_ptr<Pet>()> petGenerator)
{
auto pet = petGenerator();
printf("add pet custom: %s\n", pet->name().c_str());
pets.push_back(pet);
}
std::vector<std::shared_ptr<Pet>> pets;
};
py::class_<Pet, PyPet, std::shared_ptr<Pet>>(m, "Pet")
.def(py::init<>()).def("name", &Pet::name)
.def_readonly("id", &PyPet::id);
py::class_<PetRoom>(m, "PetRoom")
.def(py::init<>())
.def("echoPetName", &PetRoom::echoPetName)
.def("addPet", &PetRoom::addPet)
.def("addPetCustom", &PetRoom::addPetCustom)
// .def_readwrite("pets", &PetRoom::pets);
.def_readwrite("pets", &PetRoom::pets, py::return_value_policy::reference);
- my python code
import libpybind
import unittest
class Dog(libpybind.Pet):
@staticmethod
def create():
print("create python dog")
return Dog()
def __init__(self):
print("construct python dog")
libpybind.Pet.__init__(self)
def name(self):
return "dog"
class TestClass(unittest.TestCase):
def setUp(self):
self.petRoom = libpybind.PetRoom()
return super().setUp()
def test_if_functional_right(self):
# self.skipTest("skip test_if_functional_right")
self.petRoom.addPetCustom(Dog.create)
for pet in self.petRoom.pets:
print("name={},id={}".format(pet.name(), pet.id))
if __name__ == "__main__":
unittest.main()
- result
create python dog
construct python dog
Pet::Pet
add pet custom: pet
name=pet,id=0
.Pet::~Pet
- my problem
- I use a std::function to create std::shared_ptr<Pet>,because I want to create python subclass in python
- I send the
Dog.createto thePetRoom.addPetCustom - The
Dog.createbeen called inPetRoom.addPetCustom,but the result shows the inst is still Pet not Dog
Reproducible example code
No response
Is this a regression? Put the last known working version here if it is.
Not a regression
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>
#include <memory>
#include <vector>
#include <string>
#include <functional>
#include <cstdio>
namespace py = pybind11;
class Pet {
public:
Pet()
{
id = makePetId();
printf("Pet::Pet\n");
}
virtual ~Pet() { printf("Pet::~Pet\n"); }
Pet(const Pet&) = delete;
Pet(Pet&&) = delete;
virtual std::string name() const { return "pet"; }
static int makePetId()
{
static int id = 0;
return id++;
}
int id = -1;
};
class PyPet : public Pet {
public:
using Pet::Pet;
std::string name() const override { PYBIND11_OVERRIDE(std::string, Pet, name, ); }
};
class PetRoom {
public:
PetRoom() = default;
void echoPetName(std::shared_ptr<Pet> pet) { printf("pet name: %s\n", pet->name().c_str()); }
void addPet(std::shared_ptr<Pet> pet)
{
printf("add pet: %s\n", pet->name().c_str());
pets.push_back(pet);
}
void addPetCustom(std::function<std::shared_ptr<Pet>()> petGenerator)
{
auto pet = petGenerator();
printf("add pet custom: %s\n", pet->name().c_str());
pets.push_back(pet);
}
std::vector<std::shared_ptr<Pet>> pets;
};
// Binding Pet class and its methods
PYBIND11_MODULE(libpybind, m) {
py::class_<Pet, PyPet, std::shared_ptr<Pet>>(m, "Pet")
.def(py::init<>())
.def("name", &Pet::name)
.def_readonly("id", &Pet::id);
py::class_<PetRoom>(m, "PetRoom")
.def(py::init<>())
.def("echoPetName", &PetRoom::echoPetName)
.def("addPet", &PetRoom::addPet)
.def("addPetCustom", &PetRoom::addPetCustom)
.def_readwrite("pets", &PetRoom::pets, py::return_value_policy::reference);
}