A macro for easier rich comparisons
It seems the discussion on python-ideas, and also the patch review, died down. So I'm posting to python-dev. A macro like this would reduce boilerplate in stdlib and third-party C extensions. It would ease porting C extensions to Python 3, where rich comparison is mandatory. #define Py_RETURN_RICHCOMPARE(val1, val2, op) \ do { \ switch (op) { \ case Py_EQ: if ((val1) == (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_NE: if ((val1) != (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_LT: if ((val1) < (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_GT: if ((val1) > (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_LE: if ((val1) <= (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_GE: if ((val1) >= (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ } \ Py_RETURN_NOTIMPLEMENTED; \ } while (0) Is any of the core devs interested in this macro? Anything I can do to help get it in? http://bugs.python.org/issue23699
Hi,
2015-04-27 16:02 GMT+02:00 Petr Viktorin
A macro like this would reduce boilerplate in stdlib and third-party C extensions. It would ease porting C extensions to Python 3, where rich comparison is mandatory.
It would be nice to have a six module for C extensions. I'm quite sure that many projects are already full of #ifdef PYTHON3 ... #else ... #endif macros.
#define Py_RETURN_RICHCOMPARE(val1, val2, op) \ do { \ switch (op) { \ case Py_EQ: if ((val1) == (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_NE: if ((val1) != (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_LT: if ((val1) < (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_GT: if ((val1) > (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_LE: if ((val1) <= (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_GE: if ((val1) >= (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ } \ Py_RETURN_NOTIMPLEMENTED; \ } while (0)
I would prefer a function for that: PyObject *Py_RichCompare(long val1, long2, int op); You should also handle invalid operator. PyUnicode_RichCompare() calls PyErr_BadArgument() in this case. Anyway, please open an issue for this idea. Victor
On Tue, Apr 28, 2015 at 11:13 AM, Victor Stinner
Hi,
2015-04-27 16:02 GMT+02:00 Petr Viktorin
: A macro like this would reduce boilerplate in stdlib and third-party C extensions. It would ease porting C extensions to Python 3, where rich comparison is mandatory.
It would be nice to have a six module for C extensions. I'm quite sure that many projects are already full of #ifdef PYTHON3 ... #else ... #endif macros.
The idea actually came from my work on such a library: http://py3c.readthedocs.org/en/latest/
#define Py_RETURN_RICHCOMPARE(val1, val2, op) \ do { \ switch (op) { \ case Py_EQ: if ((val1) == (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_NE: if ((val1) != (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_LT: if ((val1) < (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_GT: if ((val1) > (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_LE: if ((val1) <= (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_GE: if ((val1) >= (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ } \ Py_RETURN_NOTIMPLEMENTED; \ } while (0)
I would prefer a function for that:
PyObject *Py_RichCompare(long val1, long2, int op);
The original version of the macro used ternary statements. This was shot down because a chain of comparisons would be slower than a case statement. (See discussion on the issue.) Wouldn't a function call also be slower? Also, a function with long arguments won't work on unsigned long or long long.
You should also handle invalid operator. PyUnicode_RichCompare() calls PyErr_BadArgument() in this case.
There are many different precedents, from ignoring this case to doing an assert. Is PyErr_BadArgument() better than returning NotImplemented?
Anyway, please open an issue for this idea.
On Apr 28, 2015, at 11:13 AM, Victor Stinner wrote:
It would be nice to have a six module for C extensions. I'm quite sure that many projects are already full of #ifdef PYTHON3 ... #else ... #endif macros.
Maybe encapsulating some of the recommendations here: https://wiki.python.org/moin/PortingToPy3k/BilingualQuickRef#Python_extensio... (We really need to collect all this information in on place.)
#define Py_RETURN_RICHCOMPARE(val1, val2, op)
I think this macro would make a nice addition to the C API. It might read better as `Py_RETURN_RICHCOMPARE(val1, op, val2)`. Cheers, -Barry
On Tue, Apr 28, 2015 at 4:59 PM, Barry Warsaw
On Apr 28, 2015, at 11:13 AM, Victor Stinner wrote:
It would be nice to have a six module for C extensions. I'm quite sure that many projects are already full of #ifdef PYTHON3 ... #else ... #endif macros.
Maybe encapsulating some of the recommendations here:
https://wiki.python.org/moin/PortingToPy3k/BilingualQuickRef#Python_extensio...
py3c (or its documentation) now has all that except REPRV (with an alias for the native string type, e.g. "PyStr", you can use that in all reprs, so REPRV strikes me as somewhat redundant).
(We really need to collect all this information in on place.)
#define Py_RETURN_RICHCOMPARE(val1, val2, op)
I think this macro would make a nice addition to the C API. It might read better as `Py_RETURN_RICHCOMPARE(val1, op, val2)`.
(val1, val2, op) mirrors richcmp and PyObject_RichCompareBool; I think a different order of arguments would just be confusing.
On 4/28/2015 2:13 AM, Victor Stinner wrote:
#define Py_RETURN_RICHCOMPARE(val1, val2, op) \
do { \ switch (op) { \ case Py_EQ: if ((val1) == (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_NE: if ((val1) != (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_LT: if ((val1) < (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_GT: if ((val1) > (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_LE: if ((val1) <= (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_GE: if ((val1) >= (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ } \ Py_RETURN_NOTIMPLEMENTED; \ } while (0)
I would prefer a function for that:
PyObject *Py_RichCompare(long val1, long2, int op); Why would you prefer a function? As a macro, when the op is a constant, most of the code would be optimized away by a decent compiler.
I suppose when the op is not a constant, then a function would save code space. So I suppose it depends on the predominant use cases.
You should also handle invalid operator. PyUnicode_RichCompare() calls PyErr_BadArgument() in this case. One can quibble over the correct error return, but the above code does handle invalid operators after the switch.
Glenn
On Tue, 2015-04-28 at 10:50 -0700, Glenn Linderman wrote:
On 4/28/2015 2:13 AM, Victor Stinner wrote:
#define Py_RETURN_RICHCOMPARE(val1, val2, op) \
do { \ switch (op) { \ case Py_EQ: if ((val1) == (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_NE: if ((val1) != (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_LT: if ((val1) < (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_GT: if ((val1) > (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_LE: if ((val1) <= (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ case Py_GE: if ((val1) >= (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \ } \ Py_RETURN_NOTIMPLEMENTED; \ } while (0)
I would prefer a function for that:
PyObject *Py_RichCompare(long val1, long2, int op); Why would you prefer a function? As a macro, when the op is a constant, most of the code would be optimized away by a decent compiler.
I suppose when the op is not a constant, then a function would save code space.
So I suppose it depends on the predominant use cases.
There's also the possibility of wrapping C++ code that uses overloaded operators: having it as a macro could allow those C++ operators to be be mapped into Python. Hope this is constructive Dave
participants (5)
-
Barry Warsaw
-
David Malcolm
-
Glenn Linderman
-
Petr Viktorin
-
Victor Stinner