-
-
Notifications
You must be signed in to change notification settings - Fork 53
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
Redesign legacy types and HPy_CAST #83
Comments
An idea which just came to my mind: instead of telling the user to define a macro like static inline HPyPointObject *HPyPointObject_Cast(HPyContext ctx, HPy h) {
return (HPyPointObject*)HPy_CastPure(ctx, h);
} And, while we are at it, we could even provide a macro to generate it: HPyCast_DEFINE_PURE(HPyPointObject_Cast, HPyPointObject); |
Regarding naming: |
FYI moving PyObject members into a separated "hidden" structure was discussed at: https://bugs.python.org/issue39573#msg366473 The idea would be to make PyObject an empty structure and allocate a new hidden "PyObjectHead" structure before a |
Implemented in #156. |
After my email to hpy-dev, I and @arigo discussed a possible solution on IRC.
Here is a summary of the solution we found:
To talk more concretely, let's start from existing Python/C code like this:
The idea is that an HPy type (as described by HPyType_Spec) can be:
Pure HPy:
struct PyPointObject
does NOT contain any header.all methods and slots are written in HPy, and
.legacy_slots == NULL
.if we obtain a
PyObject *
(e.g. viaHPy_AsPyObject()
), it is NOT possible to cast it toPyPointObject *
.Legacy type:
struct PyPointObject
MUST start withPyObject_HEAD
HPyType_Spec
MUST contain:.legacy_headersize = offsetof(PyPointObject, x)
, (wherex
is the first field) and it is allowed to use.legacy_slots
It is possible to cast
PyObject *
toPyPointObject *
Properties of this solution:
We pay the space penalty for
PyObject_HEAD
only if it's explicitly requestedWhen we kill
PyObject_HEAD
,.legacy_headersize
becomes automatically 0, andHPyType_FromSpec
will immediately complain if we are still using.legacy_slots
.The C-casts inside legacy methods of the type (such as
Point_foo
above) are automatically valid, since if we killPyObject_HEAD
we can't have legacy methods (as explained above)The remaining problem of this solution is how to "cast" an HPy handle into a PyPointObject* efficiently: Currently, we have
_HPy_Cast()
, but it is impossible to implement it efficiently, because we need to know whether thetype is pure or legacy, and we don't want to pay the cost of looking up the type at runtime. So, we kill
_HPy_Cast
and introduce two new API functions:HPy_CastPure
andHPy_CastLegacy
. Both of them can be implemented efficiently at runtime, and as an additionaly bonuns we can actively check that we are not calling them on the wrong type in debug mode.In order to simplify daily usage, we will introduce/document the convention that the author of
PyPointObject *
will also define a macro:This way, the rest of the code can simply call
PyPointObject_CAST
without having to worry whether it's a pure or legacy type. Then, when the porting is completed we can killPyObject_HEAD
and simply adapt the macro to useHPy_CastPure
. If we forget and/or use the wrong call toHPy_Cast*
, the debug mode will catch the error very soon.There is a remaining proble, though. The C-casts in unrelated methods (such as
bar
above) are problematic: there is no nice/automatic way to detect/avoid/emit a warning in case of a cast fromPyObject *
toPyPointObject *
. Once we killPyObject_HEAD
, all those casts become invalid and will result in subtle bugs/segfaults. One easy way to mitigate the problem is to renamestruct PyPointObject
into e.g.struct HPyPointObject
, andtypedef HPyPointObject PyPointObject
: the new hpy-friendly code will useHPyPointObject
, while the old code can continue to usePyPointObject
: when we finally killPyObject_HEAD
we also need to kill the typedef: this way, if by chance there is any code around which still casts toPyPointObject
, we will get a nice compilation error.So to summarize, the hpy-ification of the original code will start like this:
Then, we can hpy-ify some of the code:
Finally, we can turn the type into a pure-hpy type:
At this point,
bar
will no longer compile becausestruct PyPointObject*
no longer exists. Assuming it's still part of legacy code which managesPyObject *
, it will need to be manually adapted in this way:Open question: should we find a better name for
HPy_Cast{Pure,Legacy}
? They don't really do a "cast" (the actual cast is done by the macro), but they are returning a pointer to the C implementation. Maybe something likeHPy_GetStructImplPtr
is more appropriate, but I can't find any good name, so suggestions are welcome.The text was updated successfully, but these errors were encountered: