pybind11 icon indicating copy to clipboard operation
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

Open ddv5 opened this issue 2 years ago • 1 comments

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.create to the PetRoom.addPetCustom
    • The Dog.create been called in PetRoom.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

ddv5 avatar Jun 20 '23 09:06 ddv5


#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);
}

ljluestc avatar Nov 23 '24 19:11 ljluestc