Topic: auto" operators syntax


Author: "SuperKoko" <tabkannaz@yahoo.fr>
Date: Sun, 9 Jul 2006 15:14:56 CST
Raw View
Andrei Polushin wrote:
> Hi,
>
> Most user-defined operators should have well-known semantics, so they
> are implemented by pattern, written so many times by many people.
> It might be quite annoying for experts, and error-prone for novices.
>
> I propose to allow operators with "auto" modifier, so that compiler is
> able to provide predefined implementation for them.
>
> It looks like the following:
>
>     class A {
>         int first;
>         int second;
>     public:
>         A(int first, int second) auto;
>
>         A& operator+=(const A& a) auto;
>         static A operator+(const A& a, const A& b) auto;
>
>         A& operator++() auto;
>         A operator++(int) auto;
>
>         bool operator==(const A& a) const auto;
>         bool operator!=(const A& a) const auto;
>
>         bool operator<(const A& a) const auto;
>         bool operator>(const A& a) const auto;
>         bool operator>=(const A& a) const auto;
>         bool operator<=(const A& a) const auto;
>     };
>
> The code to be generated is shown in /*...*/ comments:
>
>     class A {
>         int first;
>         int second;
>     public:
>         A(int first, int second) auto;
>         /*
>             : first(first), second(second) {}
>         */
>
>         A& operator+=(const A& a) auto;
>         /*
>             first += a.first;
>             second += a.second;
>             return *this;
>         */
>
>         static A operator+(const A& a, const A& b) auto;
>         /*
>             A tmp = a;
>             tmp += b;
>             return tmp;
>         */
>
>         A& operator++() auto;
>         /*
>             ++first;
>             ++second;
>             return *this;
>         */
>
>         A operator++(int) auto;
>         /*
>             A tmp = *this;
>             operator++();
>             return tmp;
>         */
>
>         bool operator==(const A& a) const auto;
>         /*
>             return first == a.first
>                 && second == a.second
>                 ;
>         */
>
>         bool operator!=(const A& a) const auto;
>         /*
>             return !(*this == a);
>         */
>
>         bool operator<(const A& a) const auto;
>         /*
>             return first < a.first
>                 || first == a.first && second < a.second
>                 ;
>         */
>
>         bool operator>(const A& a) const auto;
>         /*
>             return a < *this;
>         */
>
>         bool operator>=(const A& a) const auto;
>         /*
>             return !(*this < a);
>         */
>
>         bool operator<=(const A& a) const auto;
>         /*
>             return !(*this > a);
>         */
>     };
>
>
> In addition, the same syntax should be allowed for main():
>
>     int main() auto;
>     /*
>         Do what I mean
>     */
>
I don't see what you mean for main.
Anyway, most of these "auto" operators are meaningless, and would never
be useful.

A much more useful system would be the automatic implementation of some
operators in terms of others.
For instance, += in terms of + and =, or + in terms of
copy-construction and +=.

Often, we get a class of objects on which an order relation is defined
: < <= == != >= > must be defined; usually in terms of a single cmp()
function which returns a negative, zero or positive value.

But, IMHO it doesn't require a core language change:
The strangely recurring template patterns used on a few well-chosen
template base classes would work well and have much more meaningful
semantics:
#include <iostream>
template <class T>
class Comparable {
 int cmp(const T& other) const {
  return static_cast<const T&>(*this).cmp(other);
 }
 public:
 bool operator <(const T& other) const {
  return cmp(other)<0;
 }
 bool operator <=(const T& other) const {
  return cmp(other)<=0;
 }
 bool operator ==(const T& other) const {
  return cmp(other)==0;
 }
 bool operator !=(const T& other) const {
  return cmp(other)!=0;
 }
 bool operator >=(const T& other) const {
  return cmp(other)>=0;
 }
 bool operator >(const T& other) const {
  return cmp(other)>0;
 }
};
template <class T,class Scalar>
/* base requirement : T must support a T::add(Scalar) method.
Rationale : T::add is a better choice than T::operator+=, because it
gives the freedom to redefine operator+= (and other operators),
independently in the T class.
Scalar must have a constructor supporting 1 as argument.
*/
class Incrementable {
 T& that() {return static_cast<T&>(*this);}
 const T& that() const {return static_cast<const T&>(*this);}
 public:
 T operator +(Scalar value) const {
  T new_object(that());
  new_object.add(value);
  return new_object;
 }
 T& operator +=(Scalar value) {
  that().add(value);
  return that();
 }
 T& operator ++() {
  that().add(Scalar(1));
  return that();
 }
 T operator ++(int) {
  T new_object(that());
  ++that();
  return new_object;
 }
};
/* you can also write a Decrementable class implementing - -= and --
and a class combining Incrementable and Decrementable features.
*/
/* You can also write an Additionable class for classes which support
self addition (i.e. T& operator+(const T&,const T&) ), in that case ++
will not be defined.
*/

class Value:public Comparable<Value>,public Incrementable<Value,int> {
 public:
 int cmp(const Value& other) const {return value-other.value;}
 void add(int incr) { /* Note : It could even be a virtual function !
*/
  value+=incr;
 }
 int value;
 Value(int val):value(val) {}
};

int main() {
 Value v0(42),v1(53);
 std::cout << (v0<v1) << (v0<=v1) << (v0==v1) << (v0!=v1) << (v0>=v1)
<< (v0>v1) << "\n";
 v0++;
 v0+=10;
 std::cout << (v0<v1) << (v0<=v1) << (v0==v1) << (v0!=v1) << (v0>=v1)
<< (v0>v1) << "\n";
 std::cout << ((++v0)+3).value << "\n";
}

Of course, you're free to define your own operator set classes.
In real life ... The same operators can have different semantics on
different classes.
Addition is a good example.
Sometimes, a class can be added a scalar (e.g. random iterators).
But, sometimes the class can be added to itself (e.g. matrixes).

In that case, there should be a template base class for "matrix-style
semantics", and another for "scalar addition semantic", more like the
example given above.

With your proposal, we must stick with a single "universal semantic",
that would impose a single view on the proper semantics of operators.

IMHO, it would be a better idea to write once all these useful template
base classes, and perhaps add them to the boost library... Or, if they
reveal to be very useful, there could be a Technical Report containing
them.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Manfred von Willich" <manfred@techniroot.co.za>
Date: Fri, 30 Jun 2006 16:04:06 CST
Raw View
Andrei Polushin wrote:
> I propose to allow operators with "auto" modifier, so that compiler is
> able to provide predefined implementation for them.

Sounded good at first

>     int main() auto;
>     /*
>         Do what I mean
>     */

until I decided that you were pulling our collective leg. (Actually,
operators == and != would be candidates for auto, defined in terms of
the same operators of base class(es) and all members, combined with &&
and || respectively.)

Manfred

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Andrei Polushin" <polushin@gmail.com>
Date: Sat, 1 Jul 2006 09:39:59 CST
Raw View
ThosRTanner wrote:
> Andrei Polushin wrote:
>>     class A {
>>         int first;
>>         int second;
>>     public:
>>         A(int first, int second) auto;
>>         /*
>>             : first(first), second(second) {}
>>         */
> Well, I can sort of see this, but I write very few classes where the
> only private data members are those initialised by the constructor.

I didn't mean /private/, but /any/ members, listed in declaration order
as constructor parameters, to be assigned to respective members.


> There's probably some sort of argument for just having
>        A(...) auto; // Equivalent to a(int first_, int second_) :
>                     //  first(first_), second(second_)

This seems to be more error-prone in case when we'll add third member.
And it's hard for documentation tool.


>>         A& operator+=(const A& a) auto;
>>         /*
>>             first += a.first;
>>             second += a.second;
>>             return *this;
>>         */
> This is not necessarily an intuitive implementation - OK for complex,
> but for other things?

Well, agreed here: it's contradictious. Should we allow only
operator+() to be auto, but require user to provide operator+=()
explicitly? This is the way of C# (but in reverse order).


> You should have also listed all the standard arithmetic operators.
>
> <snip>
>>         A& operator++() auto;
>>         /*
>>             ++first;
>>             ++second;
>>             return *this;
>>         */
> I cannot believe this to be the case for any class with more than one
> data member. I think A& operator++() { *this += 1; return *this } might
> be more useful.

It seems to be more correct, thank you.


>>         bool operator<(const A& a) const auto;
>>         /*
>>             return first < a.first
>>                 || first == a.first && second < a.second
>>                 ;
>>         */
> Umm. This doesn't seem to be compatible with your ++.

But it's OK with your correction.


> Anyway, IIRC, there are some templates in boost that allow you to do
> that sort of thing.

Yes, and those templates are "curiously recurring", which is neither
simple nor safe pattern - would you recommend them for novices?

Anyway, I can do it manually.


--
Andrei Polushin

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Andrei Polushin" <polushin@gmail.com>
Date: Sat, 1 Jul 2006 09:41:37 CST
Raw View
Manfred von Willich wrote:
> Andrei Polushin wrote:
>> I propose to allow operators with "auto" modifier, so that compiler is
>> able to provide predefined implementation for them.
>
> Sounded good at first
>
>>     int main() auto;
>>     /*
>>         Do what I mean
>>     */
>
> until I decided that you were pulling our collective leg.

Not at all. Actually, I /mean/ there are too many upcoming proposals
involving the keyword "auto", so we may expect such oddity sometimes.
This was a sort of self-criticism.

By the way, should there be a room for implementation-defined humor
in the standard?


> (Actually, operators == and != would be candidates for auto, defined
> in terms of the same operators of base class(es) and all members,
> combined with && and || respectively.)

Yes, base classes are implied as "members".

But I think it's better to define operator!=() as { !operator==() } -
it looks clearer.


--
Andrei Polushin

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Manfred von Willich" <manfred@techniroot.co.za>
Date: Sun, 2 Jul 2006 12:55:52 CST
Raw View
Andrei Polushin wrote:
> Manfred von Willich wrote:
> > until I decided that you were pulling our collective leg.
>
> Not at all. Actually, I /mean/ there are too many upcoming proposals
> involving the keyword "auto", so we may expect such oddity sometimes.
> This was a sort of self-criticism.
>
> By the way, should there be a room for implementation-defined humor
> in the standard?

Okay, I am glad the intent I read into your suggestion was a
misinterpretation.  Humor?  Yes, I think we need lots of that
navigating around all the restrictions implicit in debating changes to
a well-established language.


> > (Actually, operators == and != would be candidates for auto, defined
> > in terms of the same operators of base class(es) and all members,
> > combined with && and || respectively.)
>
> Yes, base classes are implied as "members".

Thanks.  I'm still a little vague on some of the terminology.


> But I think it's better to define operator!=() as { !operator==() } -
> it looks clearer.

This opens a whole new can of worms (not that it shouldn't be opened).
C++ allows (and requires) so many intuitively related concepts to be
defined independently, such as copy-ctor and assignment, const and
non-const versions of a member function, operators + and +=, etc.

Your suggestion has the advantage that you can define ==, and then get
!= as an auto.  But this would be inconsistent with the many other
cases that already have a compiler-defined version - for example, a
copy ctor that combines the default ctor and assignment would have been
more generally useful than using the member ctors.  Besides, one might
argue that != shouldn't be given second-class status.

One also has to think through the defined behaviour if the operators
concerned return a non-bool (e.g. Boost's tribool).  Depending on the
semantics of &&, ||, !, ==, != etc. of the member objects, this might
become tricky.  I suspect that with tribool this would make no
difference, but this will not be the case for all classes.  It is
necessary to determine whether these non-bool operators could be used,
whether the return values would first be converted to bool (to allow
use of the built-in operators like &&) etc.  Then, too, one must
realize that the semantics of most operators is actually free to
immense lattitude.  Think of std::cout::operator<<(...) - it has
nothing to do with "left shift".

If safe, intuitively obvious "auto" definitions were more clearcut, I
would lend more support.  However, I think that it probably makes more
sense to tackle the issue of unifying definitions of semi-redundant
related operators first, though probably difficult at this late stage.
Even my suggestion for == and != suffers from the shortcoming of having
to rely on effectively unrelated operators (&& and ||).

Manfred

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Andrei Polushin" <polushin@gmail.com>
Date: Thu, 29 Jun 2006 17:20:51 CST
Raw View
Hi,

Most user-defined operators should have well-known semantics, so they
are implemented by pattern, written so many times by many people.
It might be quite annoying for experts, and error-prone for novices.

I propose to allow operators with "auto" modifier, so that compiler is
able to provide predefined implementation for them.

It looks like the following:

    class A {
        int first;
        int second;
    public:
        A(int first, int second) auto;

        A& operator+=(const A& a) auto;
        static A operator+(const A& a, const A& b) auto;

        A& operator++() auto;
        A operator++(int) auto;

        bool operator==(const A& a) const auto;
        bool operator!=(const A& a) const auto;

        bool operator<(const A& a) const auto;
        bool operator>(const A& a) const auto;
        bool operator>=(const A& a) const auto;
        bool operator<=(const A& a) const auto;
    };

The code to be generated is shown in /*...*/ comments:

    class A {
        int first;
        int second;
    public:
        A(int first, int second) auto;
        /*
            : first(first), second(second) {}
        */

        A& operator+=(const A& a) auto;
        /*
            first += a.first;
            second += a.second;
            return *this;
        */

        static A operator+(const A& a, const A& b) auto;
        /*
            A tmp = a;
            tmp += b;
            return tmp;
        */

        A& operator++() auto;
        /*
            ++first;
            ++second;
            return *this;
        */

        A operator++(int) auto;
        /*
            A tmp = *this;
            operator++();
            return tmp;
        */

        bool operator==(const A& a) const auto;
        /*
            return first == a.first
                && second == a.second
                ;
        */

        bool operator!=(const A& a) const auto;
        /*
            return !(*this == a);
        */

        bool operator<(const A& a) const auto;
        /*
            return first < a.first
                || first == a.first && second < a.second
                ;
        */

        bool operator>(const A& a) const auto;
        /*
            return a < *this;
        */

        bool operator>=(const A& a) const auto;
        /*
            return !(*this < a);
        */

        bool operator<=(const A& a) const auto;
        /*
            return !(*this > a);
        */
    };


In addition, the same syntax should be allowed for main():

    int main() auto;
    /*
        Do what I mean
    */


--
Andrei Polushin

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "ThosRTanner" <ttanner2@bloomberg.net>
Date: Fri, 30 Jun 2006 07:54:27 CST
Raw View
Andrei Polushin wrote:
> Hi,
>
> Most user-defined operators should have well-known semantics, so they
> are implemented by pattern, written so many times by many people.
> It might be quite annoying for experts, and error-prone for novices.
They have well defined semantics in certain situations, and in those
situations, I agree the lack of compiler support is really aggravating.

> I propose to allow operators with "auto" modifier, so that compiler is
> able to provide predefined implementation for them.
>
> It looks like the following:
>
>     class A {
>         int first;
>         int second;
>     public:
>         A(int first, int second) auto;
>
>         A& operator+=(const A& a) auto;
>         static A operator+(const A& a, const A& b) auto;
>
>         A& operator++() auto;
>         A operator++(int) auto;
>
>         bool operator==(const A& a) const auto;
>         bool operator!=(const A& a) const auto;
>
>         bool operator<(const A& a) const auto;
>         bool operator>(const A& a) const auto;
>         bool operator>=(const A& a) const auto;
>         bool operator<=(const A& a) const auto;
>     };
>
> The code to be generated is shown in /*...*/ comments:
>
>     class A {
>         int first;
>         int second;
>     public:
>         A(int first, int second) auto;
>         /*
>             : first(first), second(second) {}
>         */
Well, I can sort of see this, but I write very few classes where the
only private data members are those initialised by the constructor.

There's probably some sort of argument for just having
       A(...) auto; //Equivalent to a(int first_, int second_) :
first(first_), second(second_)
>
>         A& operator+=(const A& a) auto;
>         /*
>             first += a.first;
>             second += a.second;
>             return *this;
>         */
This is not necessarily an intuitive implementation - OK for complex,
but for other things?

You should have also listed all the standard arithmetic operators.

<snip>
>         A& operator++() auto;
>         /*
>             ++first;
>             ++second;
>             return *this;
>         */
I cannot believe this to be the case for any class with more than one
data member. I think A& operator++() { *this += 1; return *this } might
be more useful.

>         bool operator<(const A& a) const auto;
>         /*
>             return first < a.first
>                 || first == a.first && second < a.second
>                 ;
>         */
Umm. This doesn't seem to be compatible with your ++.

Anyway, IIRC, there are some templates in boost that allow you to do
that sort of thing.

>
> In addition, the same syntax should be allowed for main():
>
>     int main() auto;
>     /*
>         Do what I mean
>     */
How on earth is the compiler meant to work out what you want to do?

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]