Skip to content

Commit 21764e4

Browse files
committed
gh-157495: Implement Py_MIN() and Py_MAX() using typeof()
If typeof() is available, implement Py_MIN(), Py_MAX() and Py_ABS() using typeof() and statement expression to only evaluate each argument once. Test also these macros in test_cext and test_cppext to test different compiler flags and test the limited C API.
1 parent e66bec0 commit 21764e4

5 files changed

Lines changed: 51 additions & 9 deletions

File tree

Include/pymacro.h

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,15 +107,35 @@
107107
# endif
108108
#endif
109109

110+
#if defined(_Py_TYPEOF) && !defined(__cplusplus)
111+
// Implement Py_MIN(), Py_MAX() and Py_ABS() using _Py_TYPEOF() and
112+
// statement expression to only evaluate each argument only once.
113+
// It cannot be used in C++: ISO C++ forbids braced-groups within
114+
// expressions.
110115

111-
/* Minimum value between x and y */
112-
#define Py_MIN(x, y) (((x) > (y)) ? (y) : (x))
113-
114-
/* Maximum value between x and y */
115-
#define Py_MAX(x, y) (((x) > (y)) ? (x) : (y))
116+
/* Minimum value between x and y */
117+
# define Py_MIN(x, y) \
118+
({ _Py_TYPEOF (x) _x = (x); \
119+
_Py_TYPEOF (y) _y = (y); \
120+
_x < _y ? _x : _y; })
121+
/* Maximum value between x and y */
122+
# define Py_MAX(x, y) \
123+
({ _Py_TYPEOF (x) _x = (x); \
124+
_Py_TYPEOF (y) _y = (y); \
125+
_x > _y ? _x : _y; })
126+
/* Absolute value of the number x */
127+
# define Py_ABS(x) \
128+
({ _Py_TYPEOF (x) _x = (x); \
129+
_x < 0 ? -_x : _x; })
130+
#else
131+
/* Minimum value between x and y */
132+
# define Py_MIN(x, y) (((x) > (y)) ? (y) : (x))
133+
/* Maximum value between x and y */
134+
# define Py_MAX(x, y) (((x) > (y)) ? (x) : (y))
135+
/* Absolute value of the number x */
136+
# define Py_ABS(x) ((x) < 0 ? -(x) : (x))
137+
#endif
116138

117-
/* Absolute value of the number x */
118-
#define Py_ABS(x) ((x) < 0 ? -(x) : (x))
119139
/* Safer implementation that avoids an undefined behavior for the minimal
120140
value of the signed integer type if its absolute value is larger than
121141
the maximal value of the signed integer type (in the two's complement

Lib/test/test_cext/extension.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,15 @@ _testcext_exec(PyObject *module)
9191
if (!result) return -1;
9292
Py_DECREF(result);
9393

94-
// test Py_BUILD_ASSERT() and Py_BUILD_ASSERT_EXPR()
94+
// Test Py_BUILD_ASSERT() and Py_BUILD_ASSERT_EXPR()
9595
Py_BUILD_ASSERT(sizeof(int) == sizeof(unsigned int));
9696
assert(Py_BUILD_ASSERT_EXPR(sizeof(int) == sizeof(unsigned int)) == 0);
9797

98+
// Test Py_MIN(), Py_MAX(), Py_ABS()
99+
assert(Py_MIN(5, 11) == 5);
100+
assert(Py_MAX(5, 11) == 11);
101+
assert(Py_ABS(-5) == 5);
102+
98103
// Test Py_CLEAR(): use typeof()/__typeof__() if available, or memcpy()
99104
obj = Py_None;
100105
Py_CLEAR(obj);

Lib/test/test_cppext/extension.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,11 @@ _testcppext_exec(PyObject *module)
294294
Py_BUILD_ASSERT(sizeof(int) == sizeof(unsigned int));
295295
assert(Py_BUILD_ASSERT_EXPR(sizeof(int) == sizeof(unsigned int)) == 0);
296296

297+
// Test Py_MIN(), Py_MAX(), Py_ABS()
298+
assert(Py_MIN(5, 11) == 5);
299+
assert(Py_MAX(5, 11) == 11);
300+
assert(Py_ABS(-5) == 5);
301+
297302
// Test Py_CLEAR(): use typeof()/__typeof__() if available, or memcpy()
298303
PyObject *obj = Py_None;
299304
Py_CLEAR(obj);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
If ``typeof()`` is available, implement :c:macro:`Py_MIN`, :c:macro:`Py_MAX`
2+
and :c:macro:`Py_ABS` using ``typeof()`` and statement expression to only
3+
evaluate each argument once. Patch by Victor Stinner.

Modules/_testcapimodule.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2045,12 +2045,21 @@ test_macros(PyObject *self, PyObject *Py_UNUSED(args))
20452045
static_assert(1 == 1, "bug");
20462046
Py_BUILD_ASSERT(1 == 1);
20472047

2048-
20492048
// Py_MIN(), Py_MAX(), Py_ABS()
20502049
assert(Py_MIN(5, 11) == 5);
20512050
assert(Py_MAX(5, 11) == 11);
20522051
assert(Py_ABS(-5) == 5);
20532052

2053+
#ifdef _Py_TYPEOF
2054+
// When _Py_TYPEOF() is available, arguments are only evaluated once
2055+
int x = 5, y = 11;
2056+
assert(Py_MIN(++x, ++y) == 6);
2057+
x = 5; y = 11;
2058+
assert(Py_MAX(++x, ++y) == 12);
2059+
x = -5;
2060+
assert(Py_ABS(--x) == 6);
2061+
#endif
2062+
20542063
// Py_STRINGIFY()
20552064
assert(strcmp(Py_STRINGIFY(123), "123") == 0);
20562065

0 commit comments

Comments
 (0)