1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
/*
tests/test_tagbased_polymorphic.cpp -- test of polymorphic_type_hook
Copyright (c) 2018 Hudson River Trading LLC <opensource@hudson-trading.com>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include <pybind11/stl.h>
#include "pybind11_tests.h"
struct Animal {
// Make this type also a "standard" polymorphic type, to confirm that
// specializing polymorphic_type_hook using enable_if_t still works
// (https://github.com/pybind/pybind11/pull/2016/).
virtual ~Animal() = default;
// Enum for tag-based polymorphism.
enum class Kind {
Unknown = 0,
Dog = 100,
Labrador,
Chihuahua,
LastDog = 199,
Cat = 200,
Panther,
LastCat = 299
};
static const std::type_info *type_of_kind(Kind kind);
static std::string name_of_kind(Kind kind);
const Kind kind;
const std::string name;
protected:
Animal(const std::string &_name, Kind _kind) : kind(_kind), name(_name) {}
};
struct Dog : Animal {
explicit Dog(const std::string &_name, Kind _kind = Kind::Dog) : Animal(_name, _kind) {}
std::string bark() const { return name_of_kind(kind) + " " + name + " goes " + sound; }
std::string sound = "WOOF!";
};
struct Labrador : Dog {
explicit Labrador(const std::string &_name, int _excitement = 9001)
: Dog(_name, Kind::Labrador), excitement(_excitement) {}
int excitement;
};
struct Chihuahua : Dog {
explicit Chihuahua(const std::string &_name) : Dog(_name, Kind::Chihuahua) {
sound = "iyiyiyiyiyi";
}
std::string bark() const { return Dog::bark() + " and runs in circles"; }
};
struct Cat : Animal {
explicit Cat(const std::string &_name, Kind _kind = Kind::Cat) : Animal(_name, _kind) {}
std::string purr() const { return "mrowr"; }
};
struct Panther : Cat {
explicit Panther(const std::string &_name) : Cat(_name, Kind::Panther) {}
std::string purr() const { return "mrrrRRRRRR"; }
};
std::vector<std::unique_ptr<Animal>> create_zoo() {
std::vector<std::unique_ptr<Animal>> ret;
ret.emplace_back(new Labrador("Fido", 15000));
// simulate some new type of Dog that the Python bindings
// haven't been updated for; it should still be considered
// a Dog, not just an Animal.
// NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
ret.emplace_back(new Dog("Ginger", Dog::Kind(150)));
ret.emplace_back(new Chihuahua("Hertzl"));
ret.emplace_back(new Cat("Tiger", Cat::Kind::Cat));
ret.emplace_back(new Panther("Leo"));
return ret;
}
const std::type_info *Animal::type_of_kind(Kind kind) {
switch (kind) {
case Kind::Unknown:
case Kind::Dog:
break;
case Kind::Labrador:
return &typeid(Labrador);
case Kind::Chihuahua:
return &typeid(Chihuahua);
case Kind::LastDog:
case Kind::Cat:
break;
case Kind::Panther:
return &typeid(Panther);
case Kind::LastCat:
break;
}
if (kind >= Kind::Dog && kind <= Kind::LastDog) {
return &typeid(Dog);
}
if (kind >= Kind::Cat && kind <= Kind::LastCat) {
return &typeid(Cat);
}
return nullptr;
}
std::string Animal::name_of_kind(Kind kind) {
std::string raw_name = type_of_kind(kind)->name();
py::detail::clean_type_id(raw_name);
return raw_name;
}
namespace PYBIND11_NAMESPACE {
template <typename itype>
struct polymorphic_type_hook<itype, detail::enable_if_t<std::is_base_of<Animal, itype>::value>> {
static const void *get(const itype *src, const std::type_info *&type) {
type = src ? Animal::type_of_kind(src->kind) : nullptr;
return src;
}
};
} // namespace PYBIND11_NAMESPACE
TEST_SUBMODULE(tagbased_polymorphic, m) {
py::class_<Animal>(m, "Animal").def_readonly("name", &Animal::name);
py::class_<Dog, Animal>(m, "Dog")
.def(py::init<std::string>())
.def_readwrite("sound", &Dog::sound)
.def("bark", &Dog::bark);
py::class_<Labrador, Dog>(m, "Labrador")
.def(py::init<std::string, int>(), "name"_a, "excitement"_a = 9001)
.def_readwrite("excitement", &Labrador::excitement);
py::class_<Chihuahua, Dog>(m, "Chihuahua")
.def(py::init<std::string>())
.def("bark", &Chihuahua::bark);
py::class_<Cat, Animal>(m, "Cat").def(py::init<std::string>()).def("purr", &Cat::purr);
py::class_<Panther, Cat>(m, "Panther")
.def(py::init<std::string>())
.def("purr", &Panther::purr);
m.def("create_zoo", &create_zoo);
}
|