-
Notifications
You must be signed in to change notification settings - Fork 1
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
Avoid C-specific Types #10
Comments
I'm not sure why this would be desirable. Do you have an example of an API where you would want to break the ABI? Also, "the few platforms" are very popular. On Linux x86-64, |
Right. I've removed that half-baked idea from the proposal. |
See also capi-workgroup/problems#27 |
In C, it's "natural" and convenient to use char, short, int, long and long long. For example, there is no PyLong_FromUint32() or PyLong_AsUint32() function. |
I'd say it's traditional. The explicitly sized types aren't really harder to use. And with C's conversion rules, |
For what it's worth, from the Rust side the default types are all fixed-size e.g.
Isn't there a risk of silent truncation lurking for users who don't exercise a bit of care here? If I understand correctly, on platforms with At least, that's what we have to do in PyO3 when we extract an |
I propose adding PyLong functions to convert to/from <stdint.h> integers: Add PyLong_FromInt64() and PyLong_AsInt64(). |
This is what we've done also in Pygolo. Initially we were just wrapping We did an extra mile so that non-sized Go In summary, we could paper over the C flexibility and give Go users what they actually need; we test our assumptions. |
IMO, we should only add those functions if they're significantly faster than
|
Yuck. Shouldn't we try to move away from macros and define actual functions instead (possibly Performance doesn't have anything to do with it. It's about convenience. |
The macros should definitely be backed by a function that does as much as possible, but |
Can you explain why they need to be in a macro? I would suggest the converse: the various functions can be implemented using a common macro, but there should be a dedicated API function for each C type of choice (mostly |
Because you can only cover a fixed number of most common types. And if we add API that's parameterized on the type (and calls |
Well, this issue is really about fixed-width integer types such as |
This issue is about avoiding types with platform-specific widths (at least for values that aren't inherently platform-specific). And now the issue is also about adding API that makes a particular alternative more usable. Perhaps we should use, for example, a |
(note: deleted comment that was based on a misunderstanding of your message; sorry!) |
That said:
I'm not sure how that is disagreeing with or countering anything I said above. |
If it's OK to discuss a proposal for |
Right, I just disagree that making |
Note that you can generate actual C API functions using a macro if you want. Here's a quick sketch: /* in .h file */
#define PY_MAKE_INT_CONVERSION_FUNCS(API_NAME, C_TYPE) \
PyAPI_FUNC(PyObject *) PyLong_From##API_NAME(C_TYPE); \
PyAPI_FUNC(C_TYPE) PyLong_As##API_NAME(PyObject *); \
PY_MAKE_INT_CONVERSION_FUNCS(Int32, int32_t);
PY_MAKE_INT_CONVERSION_FUNCS(Int64, int64_t);
PY_MAKE_INT_CONVERSION_FUNCS(UInt32, uint32_t);
PY_MAKE_INT_CONVERSION_FUNCS(UInt64, uint64_t);
#undef PY_MAKE_INT_CONVERSION_FUNCS
/* in .c file */
#define PY_MAKE_INT_CONVERSION_FUNCS(API_NAME, C_TYPE) \
PyObject *PyLong_From##API_NAME(C_TYPE) { ... actual implementation ... } \
C_TYPE PyLong_As##API_NAME(PyObject *) { ... actual implementation ... } \
PY_MAKE_INT_CONVERSION_FUNCS(Int32, int32_t);
PY_MAKE_INT_CONVERSION_FUNCS(Int64, int64_t);
PY_MAKE_INT_CONVERSION_FUNCS(UInt32, uint32_t);
PY_MAKE_INT_CONVERSION_FUNCS(UInt64, uint64_t);
#undef PY_MAKE_INT_CONVERSION_FUNCS |
I am not suggesting to make I want the size to be an argument; not to add functions for all possible values of that argument. |
To call it, you would have to write |
Unfortunately, But the hope of |
Actually, the I'm still interested in a generic solution that would cover all integral types; we can't cover them all. And even if we shoot the idea down, we should shoot down the best version of it.
Unless we use C99: #define PyLong_FROM(TYPE, VALUE) ( \
(((TYPE)-1) < (TYPE)0) \
? PyLong_FromNativeBytes(&(TYPE){VALUE}, sizeof(TYPE), -1) \
: PyLong_FromUnsignedNativeBytes(&(TYPE){VALUE}, sizeof(TYPE), -1) \
) It still needs the type for signedness; and it'd be good to add a check that How bad is to use a C99 designated initializer? Perhaps not that bad. If it's in a macro, it won't be a syntax error if you don't use it. It'll only hurt Python-3.14-only projects on a pre-C99 (or C++) compiler. But, I guess that's all moot -- it's not that much of an improvement over using The
But with a click for strawman codestatic inline int
_PyLong_To_impl(PyObject *pylong, void *buffer,
Py_ssize_t n_bytes, int flags,
Py_ssize_t type_size,
const char *typename)
{
if (n_bytes != type_size) {
PyErr_Format(PyExc_SystemError,
"PyLong_TO: size of argument does not match %s",
typename);
}
int result = PyLong_AsNativeBytes(pylong, buffer, n_bytes, flags);
if (result < 0) {
return result;
}
if (result > n_bytes) {
PyErr_Format(PyExc_ValueError,
"python integer too large to convert to %s",
typename);
return -1;
}
return result;
}
#define PyLong_TO(TYPE, DEST, N) ( \
(((TYPE)-1) < (TYPE)0) \
? _PyLong_To_impl((PyObject*)(N), &(DEST), sizeof(DEST), -1, \
sizeof(TYPE), #TYPE) \
: _PyLong_To_impl((PyObject*)(N), &(DEST), sizeof(DEST), \
Py_ASNATIVEBYTES_NATIVE_ENDIAN \
| Py_ASNATIVEBYTES_UNSIGNED_BUFFER, \
sizeof(TYPE), #TYPE) \
) I kinda wish |
int func(int *ptr)
{
return (*ptr == 3);
}
int main()
{
return func(&(int)3);
} Output:
We used multiple "magic" macros like that in the past, and it created multiple problems. It took years to fix macros and/or move away from macros. PEP 670 gives examples of macro issues. I would prefer to not introduce a new "magic" macro. I don't see advantages of this macro for the users of the API. It looks more complicated to use rather than just calling a regular function. |
The only bit it doesn't do is set the exception when the value exceeds the provided buffer. And that does make it easier, because callers are more likely to try a different approach than to simply bubble up our exception, so they're saved from a
Agreed. Using macros in our |
My PR python/cpython#120390 is now ready for review. It adds 8 functions:
|
I created capi-workgroup/decisions#32 "Add PyLong_FromInt64() and PyLong_ToInt64()" in the C API WG Decisions project. |
In new API, let's avoid types whose size/layout depends on the C compiler.
long
, bitfields, enumssize_t
,intptr_t
, which depend directly on the platformint
for small ranges (as replacement for enum) -- here practicality beats purityint32_t
etc.In the existing API, we might maybe want to e.g. replacelong
byint32_t
, and declare an ABI break on the few platforms wheresizeof(long) != sizeof(int32_t)
.The text was updated successfully, but these errors were encountered: