Efficient mechanism to generate operator overload functions I'm attempting to tidy up a C++ framework (PyCXX).

One particular file contains ~400 lines of operator overload functions, which I've managed to reduce to the following:

#define OP( op, l, r, cmpL, cmpR ) \
    bool operator op( l, r ) { return cmpL op cmpR; }

#define OPS( l, r, cmpL, cmpR ) \
    OP( !=, l, r, cmpL, cmpR ) \
    OP( ==, l, r, cmpL, cmpR ) \
    OP( &gt; , l, r, cmpL, cmpR ) \
    OP( &gt;=, l, r, cmpL, cmpR ) \
    OP( &lt; , l, r, cmpL, cmpR ) \
    OP( &lt;=, l, r, cmpL, cmpR )

    OPS( const Long &amp;a, const Long &amp;b,   a.as_long() , b.as_long() )
    OPS( const Long &amp;a,       int   b,   a.as_long() , b           )
    OPS( const Long &amp;a,       long  b,   a.as_long() , b           )
    OPS( int         a, const Long &amp;b,   a           , b.as_long() )
    OPS( long        a, const Long &amp;b,   a           , b.as_long() )

#ifdef HAVE_LONG_LONG
    OPS( const Long &amp;a, PY_LONG_LONG b,   a.as_long_long() , b                )
    OPS( PY_LONG_LONG a, const Long &amp;b,   a                , b.as_long_long() )
#endif

    //------------------------------------------------------------
    // compare operators
    OPS( const Float &amp;a, const Float &amp;b,   a.as_double() , b.as_double() )
    OPS( const Float &amp;a, double       b,   a.as_double() , b             )
    OPS( double a,       const Float &amp;b,   a             , b.as_double() )

}    // end of namespace Py


However, I wonder if it may be possible to tidy it up further.

It looks as though I can save having to pass all the parameters each time by using:

#define OPS( ... ) \
    OP( !=, ##__VA_ARGS__ ) \
    OP( ==, ##__VA_ARGS__ ) \
    OP( &gt; , ##__VA_ARGS__ ) \
    OP( &gt;=, ##__VA_ARGS__ ) \
    OP( &lt; , ##__VA_ARGS__ ) \
    OP( &lt;=, ##__VA_ARGS__ )


Although I have a hunch this might be making the code less transparent.

Another possible idea would involve storing pairs:

#define FD(op, x) (       Float &amp;x , op, x.as_double()    )
#define L1(op, x) ( const Long &amp;x  , op, x.as_long()      )
#define L2(op, x) ( const Long &amp;x  , op, x.as_long_long() )
:


but I am risking obfuscation through trying too hard.  I think it is commonly accepted that other things being equal, readability trumps concise.

What other viable design patterns could I consider?
I was looking a bit at your PyCXX and want to offer you Barton–Nackman trick using Curiously recurring template pattern together with variadic template recursion (through partial specialization). This way you can solve all your types and conversion operators with one template:

template&lt;typename T&gt; struct Arithmetic;
using Long = Arithmetic&lt;long long&gt;;
using Double = Arithmetic&lt;long double&gt;;

template&lt;class Base, class... Other&gt; struct compare_operators {};

template&lt;class Base, class First, class... Other&gt;
  struct compare_operators&lt;Base, First, Other...&gt;
  : compare_operators&lt;Base, Other...&gt;
{
    friend bool operator == (const Base&amp; a, First b) {
        return a.template as&lt;First&gt;() == b; }
    friend bool operator != (const Base&amp; a, First b) {
        return a.template as&lt;First&gt;() != b; }
    friend bool operator &lt;  (const Base&amp; a, First b) {
        return a.template as&lt;First&gt;() &lt;  b; }
    friend bool operator &lt;= (const Base&amp; a, First b) {
        return a.template as&lt;First&gt;() &lt;= b; }
    friend bool operator &gt;  (const Base&amp; a, First b) {
        return a.template as&lt;First&gt;() &gt;  b; }
    friend bool operator &gt;= (const Base&amp; a, First b) {
        return a.template as&lt;First&gt;() &gt;= b; }

    friend bool operator == (const Base&amp; a, const Arithmetic&lt;First&gt;&amp; b) {
        return a.template as&lt;First&gt;() == b.value; }
    friend bool operator != (const Base&amp; a, const Arithmetic&lt;First&gt;&amp; b) {
        return a.template as&lt;First&gt;() != b.value; }
    friend bool operator &lt;  (const Base&amp; a, const Arithmetic&lt;First&gt;&amp; b) {
        return a.template as&lt;First&gt;() &lt;  b.value; }
    friend bool operator &lt;= (const Base&amp; a, const Arithmetic&lt;First&gt;&amp; b) {
        return a.template as&lt;First&gt;() &lt;= b.value; }
    friend bool operator &gt;  (const Base&amp; a, const Arithmetic&lt;First&gt;&amp; b) {
        return a.template as&lt;First&gt;() &gt;  b.value; }
    friend bool operator &gt;= (const Base&amp; a, const Arithmetic&lt;First&gt;&amp; b) {
        return a.template as&lt;First&gt;() &gt;= b.value; }
};

template&lt;typename T&gt; struct Arithmetic
  : compare_operators&lt;Arithmetic&lt;T&gt;
  , long long, int, short, double, float, long double&gt;
{
    T value;
    Arithmetic(T value): value(value) {}

    template&lt;typename X, typename = enable_if_t&lt;is_arithmetic&lt;decay_t&lt;X&gt;&gt;::value&gt;&gt;
      X as() const { return (X)value; }
};

int main() {
    Long val(3);
    if(val &gt; 2) cout &lt;&lt; "val &gt; 2\n";
    if(val &lt;= 3.2) cout &lt;&lt; "val &lt;= 3.2\n";
    Double dbl(3.14);
    if(val &lt; dbl) cout &lt;&lt; "val &lt; dbl\n";
}


Beware that the comparision operators need some fine-tuning especially when you compare integral vs. floating-point (which to choose as common type) and that dacay_t in conversion operator may be problematic (not sure). (Finally imagine using namespace std, but I use my own header while explicitly using selected std features/classes in the namespace and then using the namespace).



BTW: This would be the same (but the as conversion is altered) in my own syntax, which you may find a bit familiar:

#include "basics.hpp"
using*firda

forward template&lt;typename T&gt; struct Arithmetic
using Long = Arithmetic&lt;long long&gt;
using Double = Arithmetic&lt;long double&gt;

template&lt;class Base, class... Other&gt;
  struct compare_operators

template&lt;class Base, class First, class... Other&gt;
  struct compare_operators&lt;Base, First, Other...&gt;
  : compare_operators&lt;Base, Other...&gt;

    friend bool operator == (const Base&amp; a, First b)
        return a.template as&lt;First&gt;() == b
    friend bool operator != (const Base&amp; a, First b)
        return a.template as&lt;First&gt;() != b
    friend bool operator &lt;  (const Base&amp; a, First b)
        return a.template as&lt;First&gt;() &lt;  b
    friend bool operator &lt;= (const Base&amp; a, First b)
        return a.template as&lt;First&gt;() &lt;= b
    friend bool operator &gt;  (const Base&amp; a, First b)
        return a.template as&lt;First&gt;() &gt;  b
    friend bool operator &gt;= (const Base&amp; a, First b)
        return a.template as&lt;First&gt;() &gt;= b

    friend bool operator == (const Base&amp; a, const Arithmetic&lt;First&gt;&amp; b)
        return a.template as&lt;First&gt;() == b.value
    friend bool operator != (const Base&amp; a, const Arithmetic&lt;First&gt;&amp; b)
        return a.template as&lt;First&gt;() != b.value
    friend bool operator &lt;  (const Base&amp; a, const Arithmetic&lt;First&gt;&amp; b)
        return a.template as&lt;First&gt;() &lt;  b.value
    friend bool operator &lt;= (const Base&amp; a, const Arithmetic&lt;First&gt;&amp; b)
        return a.template as&lt;First&gt;() &lt;= b.value
    friend bool operator &gt;  (const Base&amp; a, const Arithmetic&lt;First&gt;&amp; b)
        return a.template as&lt;First&gt;() &gt;  b.value
    friend bool operator &gt;= (const Base&amp; a, const Arithmetic&lt;First&gt;&amp; b)
        return a.template as&lt;First&gt;() &gt;= b.value

template&lt;typename T&gt; struct Arithmetic
  : compare_operators&lt;Arithmetic&lt;T&gt;
  , long long, int, short, double, float, long double&gt;

    T value
    Arithmetic(T value): value(value) {}

    template&lt;typename X&gt;
      enable_if_t&lt;is_arithmetic&lt;X&gt;::value,
      X&gt; as() const
        return value

int main()
    Long val(3)
    if val &gt; 2; cout &lt;&lt; "val &gt; 2\n"
    if val &lt;= 3.2; cout &lt;&lt; "val &lt;= 3.2\n"
    Double dbl(3.14)
    if val &lt; dbl; cout &lt;&lt; "val &lt; dbl\n"