aboutsummaryrefslogtreecommitdiff
path: root/tests/test_custom_type_casters.cpp
blob: b4af02a45222c2188df93d917e88dfb62e75d33d (plain)
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/*
    tests/test_custom_type_casters.cpp -- tests type_caster<T>

    Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>

    All rights reserved. Use of this source code is governed by a
    BSD-style license that can be found in the LICENSE file.
*/

#include "constructor_stats.h"
#include "pybind11_tests.h"

// py::arg/py::arg_v testing: these arguments just record their argument when invoked
class ArgInspector1 {
public:
    std::string arg = "(default arg inspector 1)";
};
class ArgInspector2 {
public:
    std::string arg = "(default arg inspector 2)";
};
class ArgAlwaysConverts {};

namespace PYBIND11_NAMESPACE {
namespace detail {
template <>
struct type_caster<ArgInspector1> {
public:
    // Classic
#ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
    PYBIND11_TYPE_CASTER(ArgInspector1, _("ArgInspector1"));
#else
    PYBIND11_TYPE_CASTER(ArgInspector1, const_name("ArgInspector1"));
#endif

    bool load(handle src, bool convert) {
        value.arg = "loading ArgInspector1 argument " + std::string(convert ? "WITH" : "WITHOUT")
                    + " conversion allowed.  "
                      "Argument value = "
                    + (std::string) str(src);
        return true;
    }

    static handle cast(const ArgInspector1 &src, return_value_policy, handle) {
        return str(src.arg).release();
    }
};
template <>
struct type_caster<ArgInspector2> {
public:
    PYBIND11_TYPE_CASTER(ArgInspector2, const_name("ArgInspector2"));

    bool load(handle src, bool convert) {
        value.arg = "loading ArgInspector2 argument " + std::string(convert ? "WITH" : "WITHOUT")
                    + " conversion allowed.  "
                      "Argument value = "
                    + (std::string) str(src);
        return true;
    }

    static handle cast(const ArgInspector2 &src, return_value_policy, handle) {
        return str(src.arg).release();
    }
};
template <>
struct type_caster<ArgAlwaysConverts> {
public:
    PYBIND11_TYPE_CASTER(ArgAlwaysConverts, const_name("ArgAlwaysConverts"));

    bool load(handle, bool convert) { return convert; }

    static handle cast(const ArgAlwaysConverts &, return_value_policy, handle) {
        return py::none().release();
    }
};
} // namespace detail
} // namespace PYBIND11_NAMESPACE

// test_custom_caster_destruction
class DestructionTester {
public:
    DestructionTester() { print_default_created(this); }
    ~DestructionTester() { print_destroyed(this); }
    DestructionTester(const DestructionTester &) { print_copy_created(this); }
    DestructionTester(DestructionTester &&) noexcept { print_move_created(this); }
    DestructionTester &operator=(const DestructionTester &) {
        print_copy_assigned(this);
        return *this;
    }
    DestructionTester &operator=(DestructionTester &&) noexcept {
        print_move_assigned(this);
        return *this;
    }
};
namespace PYBIND11_NAMESPACE {
namespace detail {
template <>
struct type_caster<DestructionTester> {
    PYBIND11_TYPE_CASTER(DestructionTester, const_name("DestructionTester"));
    bool load(handle, bool) { return true; }

    static handle cast(const DestructionTester &, return_value_policy, handle) {
        return py::bool_(true).release();
    }
};
} // namespace detail
} // namespace PYBIND11_NAMESPACE

// Define type caster outside of `pybind11::detail` and then alias it.
namespace other_lib {
struct MyType {};
// Corrupt `py` shorthand alias for surrounding context.
namespace py {}
// Corrupt unqualified relative `pybind11` namespace.
namespace PYBIND11_NAMESPACE {}
// Correct alias.
namespace py_ = ::pybind11;
// Define caster. This is effectively no-op, we only ensure it compiles and we
// don't have any symbol collision when using macro mixin.
struct my_caster {
    PYBIND11_TYPE_CASTER(MyType, py_::detail::const_name("MyType"));
    bool load(py_::handle, bool) { return true; }

    static py_::handle cast(const MyType &, py_::return_value_policy, py_::handle) {
        return py_::bool_(true).release();
    }
};
} // namespace other_lib
// Effectively "alias" it into correct namespace (via inheritance).
namespace PYBIND11_NAMESPACE {
namespace detail {
template <>
struct type_caster<other_lib::MyType> : public other_lib::my_caster {};
} // namespace detail
} // namespace PYBIND11_NAMESPACE

TEST_SUBMODULE(custom_type_casters, m) {
    // test_custom_type_casters

    // test_noconvert_args
    //
    // Test converting.  The ArgAlwaysConverts is just there to make the first no-conversion pass
    // fail so that our call always ends up happening via the second dispatch (the one that allows
    // some conversion).
    class ArgInspector {
    public:
        ArgInspector1 f(ArgInspector1 a, ArgAlwaysConverts) { return a; }
        std::string g(const ArgInspector1 &a,
                      const ArgInspector1 &b,
                      int c,
                      ArgInspector2 *d,
                      ArgAlwaysConverts) {
            return a.arg + "\n" + b.arg + "\n" + std::to_string(c) + "\n" + d->arg;
        }
        static ArgInspector2 h(ArgInspector2 a, ArgAlwaysConverts) { return a; }
    };
    // [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works.
    py::class_<ArgInspector>(m, "ArgInspector")
        .def(py::init<>())
        .def("f", &ArgInspector::f, py::arg(), py::arg() = ArgAlwaysConverts())
        .def("g",
             &ArgInspector::g,
             "a"_a.noconvert(),
             "b"_a,
             "c"_a.noconvert() = 13,
             "d"_a = ArgInspector2(),
             py::arg() = ArgAlwaysConverts())
        .def_static("h", &ArgInspector::h, py::arg{}.noconvert(), py::arg() = ArgAlwaysConverts());
    m.def(
        "arg_inspect_func",
        [](const ArgInspector2 &a, const ArgInspector1 &b, ArgAlwaysConverts) {
            return a.arg + "\n" + b.arg;
        },
        py::arg{}.noconvert(false),
        py::arg_v(nullptr, ArgInspector1()).noconvert(true),
        py::arg() = ArgAlwaysConverts());

    m.def(
        "floats_preferred", [](double f) { return 0.5 * f; }, "f"_a);
    m.def(
        "floats_only", [](double f) { return 0.5 * f; }, "f"_a.noconvert());
    m.def(
        "ints_preferred", [](int i) { return i / 2; }, "i"_a);
    m.def(
        "ints_only", [](int i) { return i / 2; }, "i"_a.noconvert());

    // test_custom_caster_destruction
    // Test that `take_ownership` works on types with a custom type caster when given a pointer

    // default policy: don't take ownership:
    m.def("custom_caster_no_destroy", []() {
        static auto *dt = new DestructionTester();
        return dt;
    });

    m.def(
        "custom_caster_destroy",
        []() { return new DestructionTester(); },
        py::return_value_policy::take_ownership); // Takes ownership: destroy when finished
    m.def(
        "custom_caster_destroy_const",
        []() -> const DestructionTester * { return new DestructionTester(); },
        py::return_value_policy::take_ownership); // Likewise (const doesn't inhibit destruction)
    m.def("destruction_tester_cstats",
          &ConstructorStats::get<DestructionTester>,
          py::return_value_policy::reference);

    m.def("other_lib_type", [](other_lib::MyType x) { return x; });
}