-
Notifications
You must be signed in to change notification settings - Fork 2.2k
feat: py::type::of<T>() and py::type::of(h) #2364
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Damn, @henryiii, you beat me to it, again ;-)
+1 for adding this!
When considering this PR over the last year, I realized this only works for class_
-registered types. So, given that we'll undoubtedly get lots of questions/issues about "why doesn't py::type<int>()
not return the int
type object?":
- Should we extend this? Yes, your documentation says it's for exposed types, so it's not strictly necessary. But I do wonder how many "bug" reports we're going to get. Plus, it would be a nice way of getting hold of the
int
type object? - Is there an easy way to do so? One possibility I can see would be to add this to
type_caster
asstatic py::object type()
(orstatic py::handle type()
) and add the current implementation totype_caster_base
? Not sure if this is a great idea, but it would a) be extensible, and b) be backwards compatible (since you'll get a compile error if thetype_caster
doesn't have a statictype
function).
Thanks!!! I've had some code that, uh, dipped into the internals for a while now 😅
EDIT: Thinking on it more, given that I already intercept I am totes fine with this only dealing with registered types. Perhaps, then renaming it to something slightly more obtuse, like |
I like the |
Yeah, I think a fail fast is achievable. Perhaps with something like this in the function body?
|
Except that we don't have that, I believe? So it'll be something like checking that a subclass of the default |
If we have it fail for now with non-user registered types, it could always be added later - changing a failure to a success is always allowed. :) |
Exactly! As long as we do it safely (at compile time), this would be perfect! |
681c849
to
fd8f6fc
Compare
This looks great, but would you mind adding an explicit discussion in the documentation (the auto-extracted docstrings IIRC do end up in the documentation as well, but rather hidden..) |
Waiting pending discussion on #2388 |
2174918
to
8a9238d
Compare
By the way, I'm open to ideas on how to get something like |
Filed #2486 ;) |
Right, but then for
That's an interesting suggestion, though, but I'm not sure. It's not really a type, right? Can you create an object using this?
But these do get mapped to the same Python type? I don't see the problem, here? There's a mismatch between C++ and Python, but you can still say "If you pass a C++ type
Agreed! Let's get the easy stuff in, just making sure it doesn't stop us from achieving the more complicated stuff :-) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks awesome!
Without verifying it could be done, I think you could have
Unless it was extremely easy, was not planning to add to this PR :) |
include/pybind11/pytypes.h
Outdated
PYBIND11_OBJECT_COMMON(type, object, PyType_Check) | ||
|
||
explicit type(handle h): type((PyObject*) Py_TYPE(h.ptr()), borrowed_t{}) {} | ||
explicit type(object ob): type((PyObject*) Py_TYPE(ob.ptr()), borrowed_t{}) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't necessary, I think? object
should be a subclass of handle
, and there's no extra refcounting going on?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, yes, some slicing, since it's not by reference. But there are precedents for that, I think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you/we choose to keep the object
overload, better to make it a const object &
, then. It avoids an unnecessary refcount.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, no, PYBIND11_OBJECT
already adds that overload.
pybind11/include/pybind11/pytypes.h
Lines 813 to 817 in cc982ac
#define PYBIND11_OBJECT(Name, Parent, CheckFun) \ | |
PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ | |
/* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ | |
Name(const object &o) : Parent(o) { } \ | |
Name(object &&o) : Parent(std::move(o)) { } |
How do we fix this such that it works in the intended way? When do we want py::type(...)
to "cast" a py::object
to a subclass, and when do we want it to actually call type(...)
? Does this mean that @henryiii's original py::type::of
is necessary, and that we can't have the py::type(obj)
interface I've been dreaming of? :-(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use PYBIND11_OBJECT_COMMON
instead of PYBIND11_OBJECT
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I'll try dropping this overload soon)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be a const handle &
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use
PYBIND11_OBJECT_COMMON
instead ofPYBIND11_OBJECT
.
🤦 How did I miss that?
Can this be a
const handle &
?
Probably, but I don't think it's necessary. handle
really is a fancy and more beautiful way of writing PyObject *
.
Sorry to open that discussion again (after first telling @henryiii to change it), but, I'm now realizing the following: If |
I think I preferred |
It looks like |
I worked on that here in #2349, but it got stalled behind the |
Good, then leave as is, then we can take advantage of that if it gets merged? |
I really liked |
Good for me. I'll try to pick that up again, soon'ish... |
Thanks everyone! |
Pybind11 2.6 introduces `pybind11::type::of<T>()` and `pybind11::type`, see pybind/pybind11#2364. We can use neither: `of<T>()` throws and `type` cannot represent `None`.
Pybind11 2.6 introduces `pybind11::type::of<T>()` and `pybind11::type`, see pybind/pybind11#2364. We can use neither: `of<T>()` throws and `type` cannot represent `None`.
This adds
py::type::of<T>()
, which provides a way to get the Python type associated with a C++ type. Currently there are two possible ways to do this (pointed out by @YannickJadoul and @bstaletic on Gitter about a year ago, IIRC), but they require usage of internal structures or the detail namespace. This provides a natural, pythonic way to get the type object without reverting to internal details.This is very useful in templated code; for example, in boost-histogram, it is needed to get the templated storage from a templated histogram.
EDIT (@YannickJadoul):
Closes #1692