GeezerVison is showing github.com — [C API] Py_ARRAY_LENGTH() macro is incompatible with g++ -std=gnu++11 · Issue #158810 · python/cpython
[C API] Py_ARRAY_LENGTH() macro is incompatible with g++ -std=gnu++11 #158810Labels buildThe build process and cross-buildThe build process and cross-buildinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)topic-C-APItype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error DescriptionBug report
Example:
#include <Python.h>
int main()
{
int array[] = {1, 2, 3};
return Py_ARRAY_LENGTH(array);
}
Building this example with g++ -std=gnu++11 example.cpp or just g++ example.cpp fails with:
In file included from Include/Python.h:70,
from x.cpp:1:
x.cpp: In function ‘int main()’:
Include/pymacro.h:217:30: error: ‘__builtin_types_compatible_p’ was not declared in this scope
217 | + Py_BUILD_ASSERT_EXPR(!__builtin_types_compatible_p(typeof(array), \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
The example builds correctly if I use -std=c++11 (g++ -std=c++11 example.cpp).
The macro is currently defined as:
#define Py_ARRAY_LENGTH(array) \
(sizeof(array) / sizeof((array)[0]) \
+ Py_BUILD_ASSERT_EXPR(!__builtin_types_compatible_p(typeof(array), \
typeof(&(array)[0]))))
#else
#define Py_ARRAY_LENGTH(array) \
(sizeof(array) / sizeof((array)[0]))
#endif
I'm not sure why __builtin_types_compatible_p() is available with -std=c++11, but not with -std=gnu++11.
I made my tests using g++ (GCC) 16.2.1 on Fedora 44.
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Linked PRs
Activitychanged the title [-]C API: Py_ARRAY_LENGTH() macro is incompatible with g++ -std=gnu++11[/-] [+][C API] Py_ARRAY_LENGTH() macro is incompatible with g++ -std=gnu++11[/+] on Oct 4, 2026da-woods commented on Oct 4, 2026 The documentation for __builtin_types_compatible_p says
Note: This construct is only available for C.
Which leaves a mystery about why it seems to work in -std=c++11 but which does imply you shouldn't be using it in c++ vstinner commented on Oct 4, 2026 @da-woods: It would be nice to be able to use __builtin_types_compatible_p() on C++ when it's supported. The problem is that I don't know how to check if the built-in function is available or not.
(...) which does imply you shouldn't be using it in c++
Well well well, a simple fix is to do exactly that: don't use __builtin_types_compatible_p() on C++. I implemented this approach in PR gh-158812. chris-eibl commented on Oct 5, 2026 Other places in the code base use _Py__has_builtin, but I don't think this buys us much here, since we know __builtin_types_compatible_p isn't supported in C++. Yet, it'll reduce the detection to #if _Py__has_builtin(__builtin_types_compatible_p).
The most natural fit in modern C++ beginning with C++17 is std::size, and AI will for sure spit out something for earlier versions, but that's all template based, and you've mentioned in #157649 (comment) that templates did not play out nicely for Py_CLEAR et al. vstinner commented on Oct 5, 2026
The example builds correctly if I use -std=c++11 (g++ -std=c++11 example.cpp).
When using -std=c++11, the __STRICT_ANSI__ macro is defined. In this case, Py_ARRAY_LENGTH() doesn't use built-in functions (it doesn't use __builtin_types_compatible_p() or typeof()).
Other places in the code base use _Py__has_builtin, but I don't think this buys us much here, since we know __builtin_types_compatible_p isn't supported in C++. Yet, it'll reduce the detection to #if _Py__has_builtin(__builtin_types_compatible_p).
__has_builtin(typeof) and __has_builtin(__builtin_types_compatible_p) are false using -std=c++11 and -std=gnu++11. So it cannot be used in C++ for Py_ARRAY_LENGTH().
The most natural fit in modern C++ beginning with C++17 is std::size, and AI will for sure spit out something for earlier versions, but that's all template based, and you've mentioned in #157649 (comment) that templates did not play out nicely for Py_CLEAR et al.
I'm sure that it's possible to write something nice using templates, but I would prefer to keep the Python C API simple, and avoid C++ templates in general.
#define Py_ARRAY_LENGTH(array) (sizeof(array) / sizeof((array)[0])) works just fine on C++.
It's just that it doesn't detect API misuse on C++. It would be nice to have, but it's ok to not detect API misuse in this case.
I just merged my PR to just use #define Py_ARRAY_LENGTH(array) (sizeof(array) / sizeof((array)[0])) in C++.
Thanks for your feedback @da-woods and @chris-eibl. Reacted by Chris Eibl Reacted by Chris Eibl
MetadataMetadatabuildThe build process and cross-buildThe build process and cross-buildinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)topic-C-APItype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error DevelopmentNo branches or pull requestsIssue actions
|