114299Sbbruce@ucdavis.edu/*
214299Sbbruce@ucdavis.edu    tests/test_tagbased_polymorphic.cpp -- test of polymorphic_type_hook
314299Sbbruce@ucdavis.edu
414299Sbbruce@ucdavis.edu    Copyright (c) 2018 Hudson River Trading LLC <opensource@hudson-trading.com>
514299Sbbruce@ucdavis.edu
614299Sbbruce@ucdavis.edu    All rights reserved. Use of this source code is governed by a
714299Sbbruce@ucdavis.edu    BSD-style license that can be found in the LICENSE file.
814299Sbbruce@ucdavis.edu*/
914299Sbbruce@ucdavis.edu
1014299Sbbruce@ucdavis.edu#include "pybind11_tests.h"
1114299Sbbruce@ucdavis.edu#include <pybind11/stl.h>
1214299Sbbruce@ucdavis.edu
1314299Sbbruce@ucdavis.edustruct Animal
1414299Sbbruce@ucdavis.edu{
1514299Sbbruce@ucdavis.edu    enum class Kind {
1614299Sbbruce@ucdavis.edu        Unknown = 0,
1714299Sbbruce@ucdavis.edu        Dog = 100, Labrador, Chihuahua, LastDog = 199,
1814299Sbbruce@ucdavis.edu        Cat = 200, Panther, LastCat = 299
1914299Sbbruce@ucdavis.edu    };
2014299Sbbruce@ucdavis.edu    static const std::type_info* type_of_kind(Kind kind);
2114299Sbbruce@ucdavis.edu    static std::string name_of_kind(Kind kind);
2214299Sbbruce@ucdavis.edu
2314299Sbbruce@ucdavis.edu    const Kind kind;
2414299Sbbruce@ucdavis.edu    const std::string name;
2514299Sbbruce@ucdavis.edu
2614299Sbbruce@ucdavis.edu  protected:
2714299Sbbruce@ucdavis.edu    Animal(const std::string& _name, Kind _kind)
2814299Sbbruce@ucdavis.edu        : kind(_kind), name(_name)
2914299Sbbruce@ucdavis.edu    {}
3014299Sbbruce@ucdavis.edu};
3114299Sbbruce@ucdavis.edu
3214299Sbbruce@ucdavis.edustruct Dog : Animal
3314299Sbbruce@ucdavis.edu{
3414299Sbbruce@ucdavis.edu    Dog(const std::string& _name, Kind _kind = Kind::Dog) : Animal(_name, _kind) {}
3514299Sbbruce@ucdavis.edu    std::string bark() const { return name_of_kind(kind) + " " + name + " goes " + sound; }
3614299Sbbruce@ucdavis.edu    std::string sound = "WOOF!";
3714299Sbbruce@ucdavis.edu};
3814299Sbbruce@ucdavis.edu
3914299Sbbruce@ucdavis.edustruct Labrador : Dog
4014299Sbbruce@ucdavis.edu{
4114299Sbbruce@ucdavis.edu    Labrador(const std::string& _name, int _excitement = 9001)
4214299Sbbruce@ucdavis.edu        : Dog(_name, Kind::Labrador), excitement(_excitement) {}
4314299Sbbruce@ucdavis.edu    int excitement;
4414299Sbbruce@ucdavis.edu};
4514299Sbbruce@ucdavis.edu
4614299Sbbruce@ucdavis.edustruct Chihuahua : Dog
4714299Sbbruce@ucdavis.edu{
4814299Sbbruce@ucdavis.edu    Chihuahua(const std::string& _name) : Dog(_name, Kind::Chihuahua) { sound = "iyiyiyiyiyi"; }
4914299Sbbruce@ucdavis.edu    std::string bark() const { return Dog::bark() + " and runs in circles"; }
5014299Sbbruce@ucdavis.edu};
5114299Sbbruce@ucdavis.edu
5214299Sbbruce@ucdavis.edustruct Cat : Animal
5314299Sbbruce@ucdavis.edu{
5414299Sbbruce@ucdavis.edu    Cat(const std::string& _name, Kind _kind = Kind::Cat) : Animal(_name, _kind) {}
5514299Sbbruce@ucdavis.edu    std::string purr() const { return "mrowr"; }
5614299Sbbruce@ucdavis.edu};
5714299Sbbruce@ucdavis.edu
5814299Sbbruce@ucdavis.edustruct Panther : Cat
5914299Sbbruce@ucdavis.edu{
6014299Sbbruce@ucdavis.edu    Panther(const std::string& _name) : Cat(_name, Kind::Panther) {}
6114299Sbbruce@ucdavis.edu    std::string purr() const { return "mrrrRRRRRR"; }
6214299Sbbruce@ucdavis.edu};
6314299Sbbruce@ucdavis.edu
6414299Sbbruce@ucdavis.edustd::vector<std::unique_ptr<Animal>> create_zoo()
6514299Sbbruce@ucdavis.edu{
6614299Sbbruce@ucdavis.edu    std::vector<std::unique_ptr<Animal>> ret;
6714299Sbbruce@ucdavis.edu    ret.emplace_back(new Labrador("Fido", 15000));
6814299Sbbruce@ucdavis.edu
6914299Sbbruce@ucdavis.edu    // simulate some new type of Dog that the Python bindings
7014299Sbbruce@ucdavis.edu    // haven't been updated for; it should still be considered
7114299Sbbruce@ucdavis.edu    // a Dog, not just an Animal.
7214299Sbbruce@ucdavis.edu    ret.emplace_back(new Dog("Ginger", Dog::Kind(150)));
7314299Sbbruce@ucdavis.edu
7414299Sbbruce@ucdavis.edu    ret.emplace_back(new Chihuahua("Hertzl"));
7514299Sbbruce@ucdavis.edu    ret.emplace_back(new Cat("Tiger", Cat::Kind::Cat));
7614299Sbbruce@ucdavis.edu    ret.emplace_back(new Panther("Leo"));
7714299Sbbruce@ucdavis.edu    return ret;
7814299Sbbruce@ucdavis.edu}
7914299Sbbruce@ucdavis.edu
8014299Sbbruce@ucdavis.educonst std::type_info* Animal::type_of_kind(Kind kind)
8114299Sbbruce@ucdavis.edu{
8214299Sbbruce@ucdavis.edu    switch (kind) {
8314299Sbbruce@ucdavis.edu        case Kind::Unknown: break;
8414299Sbbruce@ucdavis.edu
8514299Sbbruce@ucdavis.edu        case Kind::Dog: break;
8614299Sbbruce@ucdavis.edu        case Kind::Labrador: return &typeid(Labrador);
8714299Sbbruce@ucdavis.edu        case Kind::Chihuahua: return &typeid(Chihuahua);
8814299Sbbruce@ucdavis.edu        case Kind::LastDog: break;
8914299Sbbruce@ucdavis.edu
9014299Sbbruce@ucdavis.edu        case Kind::Cat: break;
9114299Sbbruce@ucdavis.edu        case Kind::Panther: return &typeid(Panther);
9214299Sbbruce@ucdavis.edu        case Kind::LastCat: break;
9314299Sbbruce@ucdavis.edu    }
9414299Sbbruce@ucdavis.edu
9514299Sbbruce@ucdavis.edu    if (kind >= Kind::Dog && kind <= Kind::LastDog) return &typeid(Dog);
9614299Sbbruce@ucdavis.edu    if (kind >= Kind::Cat && kind <= Kind::LastCat) return &typeid(Cat);
9714299Sbbruce@ucdavis.edu    return nullptr;
9814299Sbbruce@ucdavis.edu}
9914299Sbbruce@ucdavis.edu
10014299Sbbruce@ucdavis.edustd::string Animal::name_of_kind(Kind kind)
10114299Sbbruce@ucdavis.edu{
10214299Sbbruce@ucdavis.edu    std::string raw_name = type_of_kind(kind)->name();
10314299Sbbruce@ucdavis.edu    py::detail::clean_type_id(raw_name);
10414299Sbbruce@ucdavis.edu    return raw_name;
10514299Sbbruce@ucdavis.edu}
10614299Sbbruce@ucdavis.edu
10714299Sbbruce@ucdavis.edunamespace pybind11 {
10814299Sbbruce@ucdavis.edu    template <typename itype>
10914299Sbbruce@ucdavis.edu    struct polymorphic_type_hook<itype, detail::enable_if_t<std::is_base_of<Animal, itype>::value>>
11014299Sbbruce@ucdavis.edu    {
11114299Sbbruce@ucdavis.edu        static const void *get(const itype *src, const std::type_info*& type)
11214299Sbbruce@ucdavis.edu        { type = src ? Animal::type_of_kind(src->kind) : nullptr; return src; }
11314299Sbbruce@ucdavis.edu    };
11414299Sbbruce@ucdavis.edu}
11514299Sbbruce@ucdavis.edu
11614299Sbbruce@ucdavis.eduTEST_SUBMODULE(tagbased_polymorphic, m) {
11714299Sbbruce@ucdavis.edu    py::class_<Animal>(m, "Animal")
11814299Sbbruce@ucdavis.edu        .def_readonly("name", &Animal::name);
11914299Sbbruce@ucdavis.edu    py::class_<Dog, Animal>(m, "Dog")
12014299Sbbruce@ucdavis.edu        .def(py::init<std::string>())
12114299Sbbruce@ucdavis.edu        .def_readwrite("sound", &Dog::sound)
12214299Sbbruce@ucdavis.edu        .def("bark", &Dog::bark);
12314299Sbbruce@ucdavis.edu    py::class_<Labrador, Dog>(m, "Labrador")
12414299Sbbruce@ucdavis.edu        .def(py::init<std::string, int>(), "name"_a, "excitement"_a = 9001)
12514299Sbbruce@ucdavis.edu        .def_readwrite("excitement", &Labrador::excitement);
12614299Sbbruce@ucdavis.edu    py::class_<Chihuahua, Dog>(m, "Chihuahua")
12714299Sbbruce@ucdavis.edu        .def(py::init<std::string>())
12814299Sbbruce@ucdavis.edu        .def("bark", &Chihuahua::bark);
12914299Sbbruce@ucdavis.edu    py::class_<Cat, Animal>(m, "Cat")
13014299Sbbruce@ucdavis.edu        .def(py::init<std::string>())
13114299Sbbruce@ucdavis.edu        .def("purr", &Cat::purr);
13214299Sbbruce@ucdavis.edu    py::class_<Panther, Cat>(m, "Panther")
13314299Sbbruce@ucdavis.edu        .def(py::init<std::string>())
13414299Sbbruce@ucdavis.edu        .def("purr", &Panther::purr);
13514299Sbbruce@ucdavis.edu    m.def("create_zoo", &create_zoo);
13614299Sbbruce@ucdavis.edu};
137