Skip to content

Add test case for issue #3514 #3701

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions include/pybind11/detail/type_caster_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -943,7 +943,9 @@ template <typename itype>
struct polymorphic_type_hook_base<itype, detail::enable_if_t<std::is_polymorphic<itype>::value>> {
static const void *get(const itype *src, const std::type_info *&type) {
type = src ? &typeid(*src) : nullptr;
return dynamic_cast<const void *>(src);
const auto *downcasted = dynamic_cast<const void *>(src);
assert(downcasted != nullptr || type == nullptr);
return downcasted;
}
};
template <typename itype, typename SFINAE = void>
Expand Down Expand Up @@ -981,7 +983,7 @@ class type_caster_base : public type_caster_generic {
const auto &cast_type = typeid(itype);
const std::type_info *instance_type = nullptr;
const void *vsrc = polymorphic_type_hook<itype>::get(src, instance_type);
if (instance_type && !same_type(cast_type, *instance_type)) {
if (instance_type && vsrc && !same_type(cast_type, *instance_type)) {
// This is a base pointer to a derived type. If the derived type is registered
// with pybind11, we want to make the full derived object available.
// In the typical case where itype is polymorphic, we get the correct
Expand Down
119 changes: 119 additions & 0 deletions tests/test_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,125 @@ TEST_SUBMODULE(class_, m) {
py::class_<OtherDuplicateNested>(gt, "OtherDuplicateNested");
py::class_<OtherDuplicateNested>(gt, "YetAnotherDuplicateNested");
});
class Creature {
public:
virtual std::string typeName() const = 0;
virtual ~Creature() = default;
};

class Animal : public Creature {
public:
Animal() { std::cout << "Animal created: " << this << std::endl; }

Animal(const Animal &animal) : Creature(animal) {
std::cout << "Animal copied: from " << &animal << " to " << this << std::endl;
}

~Animal() { std::cout << "Animal deleted: " << this << std::endl; }

std::string typeName() const override { return _typeName; }

private:
std::string _typeName = "animal";
};

class TerrestrialAnimal : public virtual Animal {
public:
TerrestrialAnimal() { std::cout << "TerrestrialAnimal created: " << this << std::endl; }

TerrestrialAnimal(const TerrestrialAnimal &animal) : Animal(animal) {
std::cout << "TerrestrialAnimal copied: from " << &animal << " to " << this
<< std::endl;
}

~TerrestrialAnimal() { std::cout << "TerrestrialAnimal deleted: " << this << std::endl; }

std::string typeName() const override { return _typeName; }

private:
std::string _typeName = "terrestrial";
};

class AquaticAnimal : public virtual Animal {
public:
AquaticAnimal() { std::cout << "AquaticAnimal created: " << this << std::endl; }

AquaticAnimal(const AquaticAnimal &animal) : Animal(animal) {
std::cout << "AquaticAnimal copied: from " << &animal << " to " << this << std::endl;
}

~AquaticAnimal() { std::cout << "AquaticAnimal deleted: " << this << std::endl; }

std::string typeName() const override { return _typeName; }

private:
std::string _typeName = "aquatic";
};

class Frog : public TerrestrialAnimal, public AquaticAnimal {
public:
Frog() { std::cout << "Frog created: " << this << std::endl; }

Frog(const Frog &animal)
: Animal(animal), TerrestrialAnimal(animal), AquaticAnimal(animal) {
std::cout << "Frog copied: from " << &animal << " to " << this << std::endl;
}

~Frog() { std::cout << "Frog deleted: " << this << std::endl; }

std::string typeName() const override { return _typeName; }

private:
std::string _typeName = "frog";
};

class AnimalUsage {
public:
AnimalUsage() { std::cout << "AnimalUsage created: " << this << std::endl; }

AnimalUsage(const AnimalUsage &u) {
std::cout << "AnimalUsage copied: from " << &u << " to " << this << std::endl;
}

~AnimalUsage() { std::cout << "AnimalUsage deleted: " << this << std::endl; }

const Animal &getAnimal() { return frog; }

const AquaticAnimal &getAquaticAnimal() { return frog; }

const Frog &getFrog() { return frog; }

private:
Frog frog;
};

py::class_<Animal> animal(m, "Animal");

animal.def(py::init<>());
animal.def("type_name", &Animal::typeName);

py::class_<TerrestrialAnimal, Animal> terrestrialAnimal(m, "TerrestrialAnimal");

terrestrialAnimal.def(py::init<>());
terrestrialAnimal.def("type_name", &TerrestrialAnimal::typeName);

py::class_<AquaticAnimal, Animal> aquaticAnimal(m, "AquaticAnimal");

aquaticAnimal.def(py::init<>());
aquaticAnimal.def("type_name", &AquaticAnimal::typeName);

py::class_<Frog, TerrestrialAnimal, AquaticAnimal> frog(m, "Frog");

frog.def(py::init<>());
frog.def("type_name", &Frog::typeName);

py::class_<AnimalUsage> animalUsage(m, "AnimalUsage");

animalUsage.def(py::init<>());
animalUsage.def(py::init<const AnimalUsage &>(), py::arg("u"));
animalUsage.def("get_animal", &AnimalUsage::getAnimal);
animalUsage.def("get_aquatic_animal", &AnimalUsage::getAquaticAnimal);
animalUsage.def("get_frog", &AnimalUsage::getFrog);
}

template <int N>
Expand Down
6 changes: 6 additions & 0 deletions tests/test_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,3 +469,9 @@ class ClassScope:
m.register_duplicate_nested_class_type(ClassScope)
expected = 'generic_type: type "YetAnotherDuplicateNested" is already registered!'
assert str(exc_info.value) == expected


def test_virtual_base():
usage = m.AnimalUsage()
a = usage.get_animal()
assert a == ""