Repository: cython/cython
Pull request: allow IS_UNSIGNED to handle const types
this adds support for constant types in the `IS_UNSIGNED` macro to fix #5301 without reducing const correctness.
This uses the same implementation used for the C++11 type_trait `std::is_unsigned`: https://en.cppreference.com/w/cpp/types/is_unsigned

Problem statement:
[BUG] `IS_UNSIGNED()` macro is applied to `const` types in test suite
### Describe the bug

Example (there are several such modules):
```
Compiler output for module memslice:
memslice.cpp:1348:37: warning: type qualifiers ignored on cast result type [-Wignored-qualifiers]
 #define IS_UNSIGNED(type) (((type) -1) > 0)
                                     ^
memslice.cpp:4250:104: note: in expansion of macro ‘IS_UNSIGNED’
 static __Pyx_TypeInfo __Pyx_TypeInfo_int__const__ = { "const int", NULL, sizeof(int const ), { 0 }, 0, IS_UNSIGNED(int const ) ? 'U' : 'I', IS_UNSIGNED(int const ), 0 };
```
We should strip off the `const`, if possible. It leads to the above warning in C++.

### Code to reproduce the behaviour:

_No response_

### Expected behaviour

_No response_

### OS

Linux (g++)

### Python version

_No response_

### Cython version

master

### Additional context

_No response_

Hints from issue discussion:

Commit message: use template for unsigned check

Patch to review:
diff --git a/Cython/Utility/Buffer.c b/Cython/Utility/Buffer.c
index 3ea9a1b90fe..6b89f20e83a 100644
--- a/Cython/Utility/Buffer.c
+++ b/Cython/Utility/Buffer.c
@@ -54,7 +54,13 @@ static void __Pyx_RaiseBufferFallbackError(void) {
 /////////////// BufferFormatStructs.proto ///////////////
 //@proto_block: utility_code_proto_before_types
 
+#ifdef __cplusplus
+template<typename T>
+struct IS_UNSIGNED_IMPL {static const bool value = T(0) < T(-1);};
+#define IS_UNSIGNED(type) IS_UNSIGNED_IMPL<type>::value
+#else
 #define IS_UNSIGNED(type) (((type) -1) > 0)
+#endif
 
 /* Run-time type information about structs used with buffers */
 struct __Pyx_StructField_;
diff --git a/Cython/Utility/Overflow.c b/Cython/Utility/Overflow.c
index 2e58ee54a66..244b90571fc 100644
--- a/Cython/Utility/Overflow.c
+++ b/Cython/Utility/Overflow.c
@@ -31,7 +31,13 @@ static int __Pyx_check_twos_complement(void) {
     }
 }
 
+#ifdef __cplusplus
+template<typename T>
+struct __PYX_IS_UNSIGNED_IMPL {static const bool value = T(0) < T(-1);};
+#define __PYX_IS_UNSIGNED(type) IS_UNSIGNED_IMPL<type>::value
+#else
 #define __PYX_IS_UNSIGNED(type) ((((type) -1) > 0))
+#endif
 #define __PYX_SIGN_BIT(type)    ((((unsigned type) 1) << (sizeof(type) * 8 - 1)))
 #define __PYX_HALF_MAX(type)    ((((type) 1) << (sizeof(type) * 8 - 2)))
 #define __PYX_MIN(type)         ((__PYX_IS_UNSIGNED(type) ? (type) 0 : 0 - __PYX_HALF_MAX(type) - __PYX_HALF_MAX(type)))


Code review comments:
diff_hunk: @@ -31,7 +31,13 @@ static int __Pyx_check_twos_complement(void) {
     }
 }
 
+#ifdef __cplusplus
+template<typename T>
+struct __PYX_IS_UNSIGNED_IMPL {static const bool value = T(0) < T(-1);};
+#define __PYX_IS_UNSIGNED(type) IS_UNSIGNED_IMPL<type>::value
path: Cython/Utility/Overflow.c
text: @user1:
```suggestion
#define __PYX_IS_UNSIGNED(type) __PYX_IS_UNSIGNED_IMPL<type>::value
```