mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-24 22:25:10 +00:00
feat: py::type::of<T>() and py::type::of(h) (#2364)
* feat: type<T>() * refactor: using py::type as class * refactor: py::object as base * wip: tigher api * refactor: fix conversion and limit API further * docs: some added notes from @EricCousineau-TRI * refactor: use py::type::of
This commit is contained in:
parent
32bb9071aa
commit
f12ec00d70
@ -1,3 +1,5 @@
|
|||||||
|
.. _type-conversions:
|
||||||
|
|
||||||
Type conversions
|
Type conversions
|
||||||
################
|
################
|
||||||
|
|
||||||
|
@ -1232,3 +1232,21 @@ appropriate derived-class pointer (e.g. using
|
|||||||
more complete example, including a demonstration of how to provide
|
more complete example, including a demonstration of how to provide
|
||||||
automatic downcasting for an entire class hierarchy without
|
automatic downcasting for an entire class hierarchy without
|
||||||
writing one get() function for each class.
|
writing one get() function for each class.
|
||||||
|
|
||||||
|
Accessing the type object
|
||||||
|
=========================
|
||||||
|
|
||||||
|
You can get the type object from a C++ class that has already been registered using:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
py::type T_py = py::type::of<T>();
|
||||||
|
|
||||||
|
You can directly use ``py::type::of(ob)`` to get the type object from any python
|
||||||
|
object, just like ``type(ob)`` in Python.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Other types, like ``py::type::of<int>()``, do not work, see :ref:`type-conversions`.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6
|
||||||
|
@ -2204,6 +2204,18 @@ object object_api<Derived>::call(Args &&...args) const {
|
|||||||
|
|
||||||
PYBIND11_NAMESPACE_END(detail)
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
type type::of() {
|
||||||
|
static_assert(
|
||||||
|
std::is_base_of<detail::type_caster_generic, detail::make_caster<T>>::value,
|
||||||
|
"py::type::of<T> only supports the case where T is a registered C++ types."
|
||||||
|
);
|
||||||
|
|
||||||
|
return type((PyObject*) detail::get_type_handle(typeid(T), true).ptr(), borrowed_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#define PYBIND11_MAKE_OPAQUE(...) \
|
#define PYBIND11_MAKE_OPAQUE(...) \
|
||||||
namespace pybind11 { namespace detail { \
|
namespace pybind11 { namespace detail { \
|
||||||
template<> class type_caster<__VA_ARGS__> : public type_caster_base<__VA_ARGS__> { }; \
|
template<> class type_caster<__VA_ARGS__> : public type_caster_base<__VA_ARGS__> { }; \
|
||||||
|
@ -19,6 +19,7 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
|||||||
/* A few forward declarations */
|
/* A few forward declarations */
|
||||||
class handle; class object;
|
class handle; class object;
|
||||||
class str; class iterator;
|
class str; class iterator;
|
||||||
|
class type;
|
||||||
struct arg; struct arg_v;
|
struct arg; struct arg_v;
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
@ -890,6 +891,21 @@ private:
|
|||||||
object value = {};
|
object value = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class type : public object {
|
||||||
|
public:
|
||||||
|
PYBIND11_OBJECT(type, object, PyType_Check)
|
||||||
|
|
||||||
|
static type of(handle h) { return type((PyObject*) Py_TYPE(h.ptr()), borrowed_t{}); }
|
||||||
|
|
||||||
|
/// Convert C++ type to py::type if previously registered. Does not convert
|
||||||
|
// standard types, like int, float. etc. yet.
|
||||||
|
// See https://github.com/pybind/pybind11/issues/2486
|
||||||
|
template<typename T>
|
||||||
|
static type of();
|
||||||
|
};
|
||||||
|
|
||||||
class iterable : public object {
|
class iterable : public object {
|
||||||
public:
|
public:
|
||||||
PYBIND11_OBJECT_DEFAULT(iterable, object, detail::PyIterable_Check)
|
PYBIND11_OBJECT_DEFAULT(iterable, object, detail::PyIterable_Check)
|
||||||
|
@ -134,6 +134,32 @@ TEST_SUBMODULE(class_, m) {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
struct Invalid {};
|
||||||
|
|
||||||
|
// test_type
|
||||||
|
m.def("check_type", [](int category) {
|
||||||
|
// Currently not supported (via a fail at compile time)
|
||||||
|
// See https://github.com/pybind/pybind11/issues/2486
|
||||||
|
// if (category == 2)
|
||||||
|
// return py::type::of<int>();
|
||||||
|
if (category == 1)
|
||||||
|
return py::type::of<DerivedClass1>();
|
||||||
|
else
|
||||||
|
return py::type::of<Invalid>();
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("get_type_of", [](py::object ob) {
|
||||||
|
return py::type::of(ob);
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("as_type", [](py::object ob) {
|
||||||
|
auto tp = py::type(ob);
|
||||||
|
if (py::isinstance<py::type>(ob))
|
||||||
|
return tp;
|
||||||
|
else
|
||||||
|
throw std::runtime_error("Invalid type");
|
||||||
|
});
|
||||||
|
|
||||||
// test_mismatched_holder
|
// test_mismatched_holder
|
||||||
struct MismatchBase1 { };
|
struct MismatchBase1 { };
|
||||||
struct MismatchDerived1 : MismatchBase1 { };
|
struct MismatchDerived1 : MismatchBase1 { };
|
||||||
|
@ -26,6 +26,40 @@ def test_instance(msg):
|
|||||||
assert cstats.alive() == 0
|
assert cstats.alive() == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_type():
|
||||||
|
assert m.check_type(1) == m.DerivedClass1
|
||||||
|
with pytest.raises(RuntimeError) as execinfo:
|
||||||
|
m.check_type(0)
|
||||||
|
|
||||||
|
assert 'pybind11::detail::get_type_info: unable to find type info' in str(execinfo.value)
|
||||||
|
assert 'Invalid' in str(execinfo.value)
|
||||||
|
|
||||||
|
# Currently not supported
|
||||||
|
# See https://github.com/pybind/pybind11/issues/2486
|
||||||
|
# assert m.check_type(2) == int
|
||||||
|
|
||||||
|
|
||||||
|
def test_type_of_py():
|
||||||
|
assert m.get_type_of(1) == int
|
||||||
|
assert m.get_type_of(m.DerivedClass1()) == m.DerivedClass1
|
||||||
|
assert m.get_type_of(int) == type
|
||||||
|
|
||||||
|
|
||||||
|
def test_type_of_py_nodelete():
|
||||||
|
# If the above test deleted the class, this will segfault
|
||||||
|
assert m.get_type_of(m.DerivedClass1()) == m.DerivedClass1
|
||||||
|
|
||||||
|
|
||||||
|
def test_as_type_py():
|
||||||
|
assert m.as_type(int) == int
|
||||||
|
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
assert m.as_type(1) == int
|
||||||
|
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
assert m.as_type(m.DerivedClass1()) == m.DerivedClass1
|
||||||
|
|
||||||
|
|
||||||
def test_docstrings(doc):
|
def test_docstrings(doc):
|
||||||
assert doc(UserType) == "A `py::class_` type for testing"
|
assert doc(UserType) == "A `py::class_` type for testing"
|
||||||
assert UserType.__name__ == "UserType"
|
assert UserType.__name__ == "UserType"
|
||||||
|
Loading…
Reference in New Issue
Block a user