www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - C++ overloaded operators and D

reply "IgorStepanov" <wazar mail.ru> writes:
Now D provides very powerfull means to link C++ code with D.
However D doesn't allow to call C++ overloaded operators.
It's very annoying, because C++ code may don't provide 
non-operator analogues.
What we know about C++ overloadable operators? Overloaded 
operator in C++ is a trivial function/method with special name. 
Thus operator[](int) differs from op_index(int) function only by 
mangle.
C++ OO have a different behaviour from D OO (for example C++ 
allows different < and > operator overloads or static function 
fro binary operators), thus we should avoud the temptation of map 
C++ OOs to D OOs, or back.

Also D provides a pragma(mangle) which allows to redefine symbol 
mangle. It takes a string argument and redefine mangle to it:
pragma(mangle, "foo") void bar();//bar.mangleof == foo

I suggest to modify pragma(mangle) to support C++ operators.
If argument of this pragma is identifier (for example cppOpAdd), 
the pragma applied to extern(C++) function or method, compiler 
mangle the function in accordance with this identifier.

//C++
struct Foo
{
     int& operator[](int);
     //another fields
};

//D
extern(C++) struct Foo
{
     pragma(mangle, cppOpIndex) ref int op_index(int);
     //another fields
}

//using:
Foo f;
f.op_index(1)++; //OK, op_index is linked with Foo::operator[]
f[1]++; //Error, no special behaviour for op_index

I think this approach is simple, doesn't modify the language, can 
be easily implemented and usefull. Destroy!
Nov 11 2014
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 11 November 2014 at 22:26:48 UTC, IgorStepanov wrote:
 Now D provides very powerfull means to link C++ code with D.
 However D doesn't allow to call C++ overloaded operators.
 It's very annoying, because C++ code may don't provide 
 non-operator analogues.
 What we know about C++ overloadable operators? Overloaded 
 operator in C++ is a trivial function/method with special name. 
 Thus operator[](int) differs from op_index(int) function only 
 by mangle.
 C++ OO have a different behaviour from D OO (for example C++ 
 allows different < and > operator overloads or static function 
 fro binary operators), thus we should avoud the temptation of 
 map C++ OOs to D OOs, or back.

 Also D provides a pragma(mangle) which allows to redefine 
 symbol mangle. It takes a string argument and redefine mangle 
 to it:
 pragma(mangle, "foo") void bar();//bar.mangleof == foo

 I suggest to modify pragma(mangle) to support C++ operators.
 If argument of this pragma is identifier (for example 
 cppOpAdd), the pragma applied to extern(C++) function or 
 method, compiler mangle the function in accordance with this 
 identifier.

 //C++
 struct Foo
 {
     int& operator[](int);
     //another fields
 };

 //D
 extern(C++) struct Foo
 {
     pragma(mangle, cppOpIndex) ref int op_index(int);
     //another fields
 }

 //using:
 Foo f;
 f.op_index(1)++; //OK, op_index is linked with Foo::operator[]
 f[1]++; //Error, no special behaviour for op_index

 I think this approach is simple, doesn't modify the language, 
 can be easily implemented and usefull. Destroy!
Why would you want to go that road ? Souldn't extern(C++) struct mangle this the right way by themselves ?
Nov 11 2014
parent reply "IgorStepanov" <wazar mail.ru> writes:
On Wednesday, 12 November 2014 at 02:37:52 UTC, deadalnix wrote:
 On Tuesday, 11 November 2014 at 22:26:48 UTC, IgorStepanov 
 wrote:
 Now D provides very powerfull means to link C++ code with D.
 However D doesn't allow to call C++ overloaded operators.
 It's very annoying, because C++ code may don't provide 
 non-operator analogues.
 What we know about C++ overloadable operators? Overloaded 
 operator in C++ is a trivial function/method with special 
 name. Thus operator[](int) differs from op_index(int) function 
 only by mangle.
 C++ OO have a different behaviour from D OO (for example C++ 
 allows different < and > operator overloads or static function 
 fro binary operators), thus we should avoud the temptation of 
 map C++ OOs to D OOs, or back.

 Also D provides a pragma(mangle) which allows to redefine 
 symbol mangle. It takes a string argument and redefine mangle 
 to it:
 pragma(mangle, "foo") void bar();//bar.mangleof == foo

 I suggest to modify pragma(mangle) to support C++ operators.
 If argument of this pragma is identifier (for example 
 cppOpAdd), the pragma applied to extern(C++) function or 
 method, compiler mangle the function in accordance with this 
 identifier.

 //C++
 struct Foo
 {
    int& operator[](int);
    //another fields
 };

 //D
 extern(C++) struct Foo
 {
    pragma(mangle, cppOpIndex) ref int op_index(int);
    //another fields
 }

 //using:
 Foo f;
 f.op_index(1)++; //OK, op_index is linked with Foo::operator[]
 f[1]++; //Error, no special behaviour for op_index

 I think this approach is simple, doesn't modify the language, 
 can be easily implemented and usefull. Destroy!
Why would you want to go that road ? Souldn't extern(C++) struct mangle this the right way by themselves ?
C++ and D provides different behaviour for operator overloading. D has a opIndex + opIndexAssign overloads, and if we want to map opIndex to operator[], we must to do something with opIndexAssign. operator< and operator> can't be mapped to D. Same for operator&. Binary arithmetic operators can't be mapped to D, if them implemented as static functions: Foo operator+(int a, Foo f); //unable to map it to D, because static module-level Foo opAdd(int, Foo) will not provide the same behaviour as operator+ in D. Thus: C++ and D overloaded operators should live in different worlds.
Nov 12 2014
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Wednesday, 12 November 2014 at 11:43:36 UTC, IgorStepanov 
wrote:
 C++ and D provides different behaviour for operator overloading.
 D has a opIndex + opIndexAssign overloads, and if we want to 
 map opIndex to operator[], we must to do something with 
 opIndexAssign.
operator[] can be mapped to opIndex just fine, right? Only opIndexAssign wouldn't be accessible from C++ via an operator, but that's because the feature doesn't exist. We can still call it via its name opIndexAssign.
 operator< and operator> can't be mapped to D. Same for 
 operator&.
That's true. Maybe we can just live with pragma(mangle) for them, but use D's op... for all others?
 Binary arithmetic operators can't be mapped to D, if them 
 implemented as static functions:

 Foo operator+(int a, Foo f); //unable to map it to D, because 
 static module-level Foo opAdd(int, Foo) will not provide the 
 same behaviour as operator+ in D.
 Thus: C++ and D overloaded operators should live in different 
 worlds.
Can't we map both static and member operators to opBinary resp. opBinaryRight members in this case? How likely is it that both are defined on the C++ side, and if they are, how likely is it that they will behave differently?
Nov 12 2014
parent reply "IgorStepanov" <wazar mail.ru> writes:
On Wednesday, 12 November 2014 at 14:41:17 UTC, Marc Schütz wrote:
 On Wednesday, 12 November 2014 at 11:43:36 UTC, IgorStepanov 
 wrote:
 C++ and D provides different behaviour for operator 
 overloading.
 D has a opIndex + opIndexAssign overloads, and if we want to 
 map opIndex to operator[], we must to do something with 
 opIndexAssign.
operator[] can be mapped to opIndex just fine, right? Only opIndexAssign wouldn't be accessible from C++ via an operator, but that's because the feature doesn't exist. We can still call it via its name opIndexAssign.
 operator< and operator> can't be mapped to D. Same for 
 operator&.
That's true. Maybe we can just live with pragma(mangle) for them, but use D's op... for all others?
 Binary arithmetic operators can't be mapped to D, if them 
 implemented as static functions:

 Foo operator+(int a, Foo f); //unable to map it to D, because 
 static module-level Foo opAdd(int, Foo) will not provide the 
 same behaviour as operator+ in D.
 Thus: C++ and D overloaded operators should live in different 
 worlds.
Can't we map both static and member operators to opBinary resp. opBinaryRight members in this case? How likely is it that both are defined on the C++ side, and if they are, how likely is it that they will behave differently?
opBinary(Right) is a template-functions. You can't add previous declaration for it to struct: //C++ struct Foo { Foo operator+(const Foo&); }; Foo operator+(int, const Foo&); //D extern(C++) struct struct Foo { Foo opBinary!"+"(const ref Foo); //??? } Foo opBinary!"+"(int, const ref Foo); //??? May be some cases can be mapped to D, but these cases require special consideration. I suggest a generic rule. extern(C++) struct struct Foo { pragma(mangle, cppOpAdd)Foo op_add(const ref Foo); } extern(C++) pragma(mangle, cppOpAdd)Foo op_add2(int, const ref Foo); Now, if you want to use this overloaded operators as D operators, you may wrap it to D operator-functions. extern(C++) struct struct Foo { pragma(mangle, cppOpAdd) Foo op_add(const ref Foo); Foo opBinary(string s)(const ref Foo rvl) if (s == "+") { return op_add(rvl); } Foo opBinaryRight(string s)(int lvl) if (s == "+") { return op_add2(lvl, this); } } extern(C++) pragma(mangle, cppOpAdd)Foo op_add2(int, const ref Foo); This way allows access to C++ operators and doesn't add new rules into the language.
Nov 12 2014
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Wednesday, 12 November 2014 at 19:32:32 UTC, IgorStepanov 
wrote:
 On Wednesday, 12 November 2014 at 14:41:17 UTC, Marc Schütz 
 wrote:
 On Wednesday, 12 November 2014 at 11:43:36 UTC, IgorStepanov 
 wrote:
 C++ and D provides different behaviour for operator 
 overloading.
 D has a opIndex + opIndexAssign overloads, and if we want to 
 map opIndex to operator[], we must to do something with 
 opIndexAssign.
operator[] can be mapped to opIndex just fine, right? Only opIndexAssign wouldn't be accessible from C++ via an operator, but that's because the feature doesn't exist. We can still call it via its name opIndexAssign.
 operator< and operator> can't be mapped to D. Same for 
 operator&.
That's true. Maybe we can just live with pragma(mangle) for them, but use D's op... for all others?
 Binary arithmetic operators can't be mapped to D, if them 
 implemented as static functions:

 Foo operator+(int a, Foo f); //unable to map it to D, because 
 static module-level Foo opAdd(int, Foo) will not provide the 
 same behaviour as operator+ in D.
 Thus: C++ and D overloaded operators should live in different 
 worlds.
Can't we map both static and member operators to opBinary resp. opBinaryRight members in this case? How likely is it that both are defined on the C++ side, and if they are, how likely is it that they will behave differently?
opBinary(Right) is a template-functions. You can't add previous declaration for it to struct: //C++ struct Foo { Foo operator+(const Foo&); }; Foo operator+(int, const Foo&); //D extern(C++) struct struct Foo { Foo opBinary!"+"(const ref Foo); //???
I see...
 }

 Foo opBinary!"+"(int, const ref Foo); //???
But this would of course be opBinaryRight, and inside struct Foo.
Nov 12 2014
parent reply "IgorStepanov" <wazar mail.ru> writes:
On Wednesday, 12 November 2014 at 20:49:42 UTC, Marc Schütz wrote:
 On Wednesday, 12 November 2014 at 19:32:32 UTC, IgorStepanov 
 wrote:
 On Wednesday, 12 November 2014 at 14:41:17 UTC, Marc Schütz 
 wrote:
 On Wednesday, 12 November 2014 at 11:43:36 UTC, IgorStepanov 
 wrote:
 C++ and D provides different behaviour for operator 
 overloading.
 D has a opIndex + opIndexAssign overloads, and if we want to 
 map opIndex to operator[], we must to do something with 
 opIndexAssign.
operator[] can be mapped to opIndex just fine, right? Only opIndexAssign wouldn't be accessible from C++ via an operator, but that's because the feature doesn't exist. We can still call it via its name opIndexAssign.
 operator< and operator> can't be mapped to D. Same for 
 operator&.
That's true. Maybe we can just live with pragma(mangle) for them, but use D's op... for all others?
 Binary arithmetic operators can't be mapped to D, if them 
 implemented as static functions:

 Foo operator+(int a, Foo f); //unable to map it to D, 
 because static module-level Foo opAdd(int, Foo) will not 
 provide the same behaviour as operator+ in D.
 Thus: C++ and D overloaded operators should live in 
 different worlds.
Can't we map both static and member operators to opBinary resp. opBinaryRight members in this case? How likely is it that both are defined on the C++ side, and if they are, how likely is it that they will behave differently?
opBinary(Right) is a template-functions. You can't add previous declaration for it to struct: //C++ struct Foo { Foo operator+(const Foo&); }; Foo operator+(int, const Foo&); //D extern(C++) struct struct Foo { Foo opBinary!"+"(const ref Foo); //???
I see...
 }

 Foo opBinary!"+"(int, const ref Foo); //???
But this would of course be opBinaryRight, and inside struct Foo.
What if Foo operator+(const Bar&, const Foo&);? Is it Foo.opBinaryRight, or Bar.opBinary, or both?
Nov 12 2014
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Wednesday, 12 November 2014 at 21:17:42 UTC, IgorStepanov 
wrote:
 On Wednesday, 12 November 2014 at 20:49:42 UTC, Marc Schütz 
 wrote:
 On Wednesday, 12 November 2014 at 19:32:32 UTC, IgorStepanov 
 wrote:
 On Wednesday, 12 November 2014 at 14:41:17 UTC, Marc Schütz 
 wrote:
 On Wednesday, 12 November 2014 at 11:43:36 UTC, IgorStepanov 
 wrote:
 C++ and D provides different behaviour for operator 
 overloading.
 D has a opIndex + opIndexAssign overloads, and if we want 
 to map opIndex to operator[], we must to do something with 
 opIndexAssign.
operator[] can be mapped to opIndex just fine, right? Only opIndexAssign wouldn't be accessible from C++ via an operator, but that's because the feature doesn't exist. We can still call it via its name opIndexAssign.
 operator< and operator> can't be mapped to D. Same for 
 operator&.
That's true. Maybe we can just live with pragma(mangle) for them, but use D's op... for all others?
 Binary arithmetic operators can't be mapped to D, if them 
 implemented as static functions:

 Foo operator+(int a, Foo f); //unable to map it to D, 
 because static module-level Foo opAdd(int, Foo) will not 
 provide the same behaviour as operator+ in D.
 Thus: C++ and D overloaded operators should live in 
 different worlds.
Can't we map both static and member operators to opBinary resp. opBinaryRight members in this case? How likely is it that both are defined on the C++ side, and if they are, how likely is it that they will behave differently?
opBinary(Right) is a template-functions. You can't add previous declaration for it to struct: //C++ struct Foo { Foo operator+(const Foo&); }; Foo operator+(int, const Foo&); //D extern(C++) struct struct Foo { Foo opBinary!"+"(const ref Foo); //???
I see...
 }

 Foo opBinary!"+"(int, const ref Foo); //???
But this would of course be opBinaryRight, and inside struct Foo.
What if Foo operator+(const Bar&, const Foo&);? Is it Foo.opBinaryRight, or Bar.opBinary, or both?
For a C++ class interfaced from D: opBinary() in whichever of the two classes it is defined. For a D class interfaced from C++: choose one, preferably opBinary(), as it's the "natural" one.
Nov 13 2014
parent "IgorStepanov" <wazar mail.ru> writes:
On Thursday, 13 November 2014 at 09:57:04 UTC, Marc Schütz wrote:
 On Wednesday, 12 November 2014 at 21:17:42 UTC, IgorStepanov 
 wrote:
 On Wednesday, 12 November 2014 at 20:49:42 UTC, Marc Schütz 
 wrote:
 On Wednesday, 12 November 2014 at 19:32:32 UTC, IgorStepanov 
 wrote:
 On Wednesday, 12 November 2014 at 14:41:17 UTC, Marc Schütz 
 wrote:
 On Wednesday, 12 November 2014 at 11:43:36 UTC, 
 IgorStepanov wrote:
 C++ and D provides different behaviour for operator 
 overloading.
 D has a opIndex + opIndexAssign overloads, and if we want 
 to map opIndex to operator[], we must to do something with 
 opIndexAssign.
operator[] can be mapped to opIndex just fine, right? Only opIndexAssign wouldn't be accessible from C++ via an operator, but that's because the feature doesn't exist. We can still call it via its name opIndexAssign.
 operator< and operator> can't be mapped to D. Same for 
 operator&.
That's true. Maybe we can just live with pragma(mangle) for them, but use D's op... for all others?
 Binary arithmetic operators can't be mapped to D, if them 
 implemented as static functions:

 Foo operator+(int a, Foo f); //unable to map it to D, 
 because static module-level Foo opAdd(int, Foo) will not 
 provide the same behaviour as operator+ in D.
 Thus: C++ and D overloaded operators should live in 
 different worlds.
Can't we map both static and member operators to opBinary resp. opBinaryRight members in this case? How likely is it that both are defined on the C++ side, and if they are, how likely is it that they will behave differently?
opBinary(Right) is a template-functions. You can't add previous declaration for it to struct: //C++ struct Foo { Foo operator+(const Foo&); }; Foo operator+(int, const Foo&); //D extern(C++) struct struct Foo { Foo opBinary!"+"(const ref Foo); //???
I see...
 }

 Foo opBinary!"+"(int, const ref Foo); //???
But this would of course be opBinaryRight, and inside struct Foo.
What if Foo operator+(const Bar&, const Foo&);? Is it Foo.opBinaryRight, or Bar.opBinary, or both?
For a C++ class interfaced from D: opBinary() in whichever of the two classes it is defined. For a D class interfaced from C++: choose one, preferably opBinary(), as it's the "natural" one.
It is too difficult, I think. 1. Compiler should generate static operator declaration (for linkage with C++) and method-wrapper. 2. We should use old non-template operators (like opAdd, opSub etc.) or introduce new kind of operators. 3. We should explain to the user how to use our operator bindings (explain to the user). Anyway we may implement generic approach with pragma(mangle) now and add special rules for some operators if it be considered usefull. AFAIK, There are many objections aganist operators mapping are mentioned in n.g.
Nov 13 2014