D - Free Operator Function Overloads
- davepermen (15/15) Jan 05 2004 Walter
- Felix (4/19) Jan 05 2004 Me too, I find a little weird the syntax Ta.opAdd(Tb) but, if I remember...
- Antti =?iso-8859-1?Q?Syk=E4ri?= (63/71) Jan 05 2004 There's no "standard" way in C++, you can pick any way you like. The
- Ben Hinkle (23/94) Jan 05 2004 I agree it would probably be better to have binary overloading decoupled
- Walter (5/10) Jan 05 2004 The problem is that if they were implemented, then there'd be a need for
- Matthew (4/15) Jan 05 2004 With you so far
- Walter (17/34) Jan 06 2004 I've done some extensive googling for info on export. It seems the main
- davepermen (2/12) Jan 06 2004
- davepermen (64/135) Jan 06 2004 i don't see how the operator overloading can result in more work to "loo...
- Walter (11/19) Jan 06 2004 up"..
- Hauke Duden (19/28) Jan 06 2004 Would that even be sufficient? Doesn't one expect to be able to write an...
- Hauke Duden (3/4) Jan 06 2004 Sorry, that should have been
- Matthew (8/26) Jan 06 2004 Absolutely. And without which, we'll all be hitting ourselves in the hea...
- Hauke Duden (28/42) Jan 06 2004 I have no idea what ADL is, but wouldn't the same problem also apply to
- Ben Hinkle (14/42) Jan 06 2004 operator
- Walter (8/20) Jan 06 2004 operator
-
Carlos Santander B.
(18/18)
Jan 06 2004
"Walter"
wrote in message - Walter (6/22) Jan 06 2004 in
- davepermen (19/42) Jan 07 2004 what bether thing do you suggest then? with the operators, we get a nice...
- Walter (12/15) Jan 07 2004 simple
- davepermen (14/29) Jan 07 2004 i do understand that. but you simply can't overload your prefered operat...
- Walter (10/22) Jan 09 2004 for
- davepermen (33/57) Jan 09 2004 operators have to be in the module you use them (sort of private operato...
- Sean L. Palmer (38/100) Jan 11 2004 Streams are another kind of iterator (input, or output).
- Andy Friesen (23/48) Jan 07 2004 It's an irk. I don't see any way to work around it within the confines
- davepermen (12/60) Jan 07 2004 but the by far most straightforward and logical way. you don't need dele...
- Andy Friesen (20/32) Jan 07 2004 There are lots of reasons not to use boost. It's big and monolithic and...
- davepermen (17/36) Jan 07 2004 this was sarcasm, dude! the hackery boost has to do to implement rather ...
- Matthew (3/6) Jan 07 2004 Well put!
- Erik Baklund (11/34) Jan 07 2004 Hi,
- Antti =?iso-8859-1?Q?Syk=E4ri?= (11/63) Jan 07 2004 And as well, the additive operation need not be commutative if you
- davepermen (12/30) Jan 07 2004 this is great. for OWN TYPES.
- Hauke Duden (9/34) Jan 07 2004 Yes, I understand that. But I still don't see how this issue is
- Ilya Minkov (11/19) Jan 07 2004 Read the manual. Overloads are only possible within one scope. That
- Hauke Duden (6/16) Jan 07 2004 I wasn't aware of that - thanks for explaining. So the problem IS the
- Matthew (3/18) Jan 07 2004 Agreed.
- Ben Hinkle (7/25) Jan 08 2004 Even if you import foo and bar?? I thought that was the whole point of
- Ilya Minkov (12/21) Jan 09 2004 Import doesn't bring a module into your scope. It only adds it to module...
- Ben Hinkle (32/53) Jan 09 2004 Importing a modules adds all the toplevel declarations to the current sc...
- Lewis (3/7) Jan 09 2004 thats good to know, thanks
- Sean L. Palmer (19/40) Jan 11 2004 What, do you have to use ..?
- Walter (10/20) May 13 2004 You can make it work by using an alias to overload a function in one
- davepermen (23/47) Jan 06 2004 if we disallow member-operators (wich i don't see useful anyways), then ...
- Matthew (9/13) Jan 06 2004 nothing
- Ilya Minkov (12/16) Jan 06 2004 I think a workaround with RTTI (safe cast) and interfaces can be found
- davepermen (1/3) Jan 06 2004 show me how.
- davepermen (3/16) Jan 06 2004 unique per type-pair
- Matthew (7/22) Jan 05 2004 Hear, hear!
- Mark T (13/28) Jan 07 2004 It seems that operator overloads should belong at the module level since...
Walter this is essencial. not only for streaming libraries, but simply in general.. an Tc = Ta + Tb doesn't have ANY preference for any of those 3 types. it should be Tc opAdd(Ta a,Tb b) {} and not Tc Ta.opAdd(Tb b) {} or Tc Tb.opAddR(Ta a) {} or even worse, both! just get rid of it. binary operators don't belong to a member. they never did. and if you release that essencial missconcept, and replace it by the only real one, we gain immediate access to a very fast implementable typesave, and extendable streaming library.. Tc operator add(Ta a,Tb b) { } would be my prefered way.
Jan 05 2004
Me too, I find a little weird the syntax Ta.opAdd(Tb) but, if I remember, this is the standard way to overload operators in C++, too. Maybe, if cannot be simply dropped, an alternative definition will be suitable. In article <btbq8o$2g9e$1 digitaldaemon.com>, davepermen says...Walter this is essencial. not only for streaming libraries, but simply in general.. an Tc = Ta + Tb doesn't have ANY preference for any of those 3 types. it should be Tc opAdd(Ta a,Tb b) {} and not Tc Ta.opAdd(Tb b) {} or Tc Tb.opAddR(Ta a) {} or even worse, both! just get rid of it. binary operators don't belong to a member. they never did. and if you release that essencial missconcept, and replace it by the only real one, we gain immediate access to a very fast implementable typesave, and extendable streaming library.. Tc operator add(Ta a,Tb b) { } would be my prefered way.
Jan 05 2004
In article <btbv9t$2p2s$1 digitaldaemon.com>, Felix wrote:Me too, I find a little weird the syntax Ta.opAdd(Tb) but, if I remember, this is the standard way to overload operators in C++, too. Maybe, if cannot be simply dropped, an alternative definition will be suitable.There's no "standard" way in C++, you can pick any way you like. The things that affect your choice are the same with operators and other functions: - only member functions/operators can be overridden in a derived class - only non-member functions can be used to extend the class after it has been closed For example, suppose that the standard output mechanism in D would be something like Printer stdout; void main() { stdout ~ "Hello world " ~ 1 ~ 2 ~ 3 ~ endl; } And Printer implemented something like: class Printer { Printer opCat(int i) { .. } Printer opCat(char[] c) { .. } Printer opCat(Object o) { .. } } [replace Printer with OutputStream if you feel very generic] then the only way to introduce a new printable user-defined type, say Vector3, that isn't derived from Object but still can be printed to stdout is to make a free-standing function: Printer opCat(Printer p, Vector3 v) { p ~ "(" ~ v.x ~ ", " ~ v.y ~ ", " ~ v.z ~ ")"; } That is, if that was possible in D. You can do that in C++. If D was an OO only language, then making types not derived from Object would not be possible and the issue would be moot. Heck, we could be as well programming in Java. But I gather D is supposed be multiparadigm and all that, and you ought to be able to do stuff also without objects if you want. I object to objects. And if there is some template code somewhere, saying "stdout ~ t" where t is of type which is argument for the template, you sure as hell want your Vector3 to be printed instead of getting a message along the lines of "Printer does not have member function opCat(Printer, Vector3). Wanna change it? You can't! Bwahaha!". Duh! So to conclude the rant, I fully agree with davepermen:just get rid of it. binary operators don't belong to a member. they never did.and Matthew:That's something we need that is supported by a large number of issues, not just this one. It's an absolute must, and I can't seee Walter escaping us on this one. :)The only potential problems I see with free-standing operators are: 1. Importing or not importing a module containing operators will affect the way functions are overloaded. "a + b" might mean a different thing after adding an import. On the other hand, normal function calls are already vulnerable for this and I haven't experienced any problems whatsoever. Nor have I had any problems with the issue in C++, where it also exists. 2. The overloading rules might be a tad more complex with freestanding operators. That is, they might require some thinking so that they don't cause any surprises. Maybe there is someone less tired to perform the thinking ;) Complexity doesn't bother me if the rules are sufficiently simple to grasp intuitively. (Unless they end up in some Snake-Tongued Two-Phased Koenig Lookup or similar maze of mirrors) If there are any other valid reasons against non-member operators, please bring them forth because I for one have forgotten them. Did the original D "vision" include them? Why/why not? Is the reason that they haven't been implemented a reason of principle or merely a practical one? By tackling those issues one by one we might even arrive at a solution that satisfies everyone. At least, one can hope... -Antti
Jan 05 2004
I agree it would probably be better to have binary overloading decoupled from methods (trading off run-time dispatching against extending classes without subclassing). But... from the D doc: "In D, function overloading is simple. It matches exactly, it matches with implicit conversions, or it does not match. If there is more than one match, it is an error." So does that mean I can't specialize an existing function like: Number opAdd(Number, int) {...} NumberSubclass opAdd(NumberSubclass,int) {...} If I have a variable of type NumberSubclass and I write "x+1" then it could match both (one exactly and the other with an implicit conversion) and so that would be an error. Or what if I had a subclass of NumberSubclass (so there would be two implicit conversion matches). It seems to me that this would make the proposed behavior of binary operator overloading less useful. Not unusable, I suppose. It would worth looking at a bunch of examples to see what would actually happen with either design and what the advantages and disadvantages of each would be. I for one can't immediately tell why one or the other would be a "no-brainer". -Ben "Antti Sykäri" <jsykari gamma.hut.fi> wrote in message news:slrnbvjcbm.69b.jsykari pulu.hut.fi...In article <btbv9t$2p2s$1 digitaldaemon.com>, Felix wrote:remember, thisMe too, I find a little weird the syntax Ta.opAdd(Tb) but, if Ibeis the standard way to overload operators in C++, too. Maybe, if cannotsimply dropped, an alternative definition will be suitable.There's no "standard" way in C++, you can pick any way you like. The things that affect your choice are the same with operators and other functions: - only member functions/operators can be overridden in a derived class - only non-member functions can be used to extend the class after it has been closed For example, suppose that the standard output mechanism in D would be something like Printer stdout; void main() { stdout ~ "Hello world " ~ 1 ~ 2 ~ 3 ~ endl; } And Printer implemented something like: class Printer { Printer opCat(int i) { .. } Printer opCat(char[] c) { .. } Printer opCat(Object o) { .. } } [replace Printer with OutputStream if you feel very generic] then the only way to introduce a new printable user-defined type, say Vector3, that isn't derived from Object but still can be printed to stdout is to make a free-standing function: Printer opCat(Printer p, Vector3 v) { p ~ "(" ~ v.x ~ ", " ~ v.y ~ ", " ~ v.z ~ ")"; } That is, if that was possible in D. You can do that in C++. If D was an OO only language, then making types not derived from Object would not be possible and the issue would be moot. Heck, we could be as well programming in Java. But I gather D is supposed be multiparadigm and all that, and you ought to be able to do stuff also without objects if you want. I object to objects. And if there is some template code somewhere, saying "stdout ~ t" where t is of type which is argument for the template, you sure as hell want your Vector3 to be printed instead of getting a message along the lines of "Printer does not have member function opCat(Printer, Vector3). Wanna change it? You can't! Bwahaha!". Duh! So to conclude the rant, I fully agree with davepermen:just get rid of it. binary operators don't belong to a member. they never did.and Matthew:That's something we need that is supported by a large number of issues, not just this one. It's an absolute must, and I can't seee Walter escaping us on this one. :)The only potential problems I see with free-standing operators are: 1. Importing or not importing a module containing operators will affect the way functions are overloaded. "a + b" might mean a different thing after adding an import. On the other hand, normal function calls are already vulnerable for this and I haven't experienced any problems whatsoever. Nor have I had any problems with the issue in C++, where it also exists. 2. The overloading rules might be a tad more complex with freestanding operators. That is, they might require some thinking so that they don't cause any surprises. Maybe there is someone less tired to perform the thinking ;) Complexity doesn't bother me if the rules are sufficiently simple to grasp intuitively. (Unless they end up in some Snake-Tongued Two-Phased Koenig Lookup or similar maze of mirrors) If there are any other valid reasons against non-member operators, please bring them forth because I for one have forgotten them. Did the original D "vision" include them? Why/why not? Is the reason that they haven't been implemented a reason of principle or merely a practical one? By tackling those issues one by one we might even arrive at a solution that satisfies everyone. At least, one can hope... -Antti
Jan 05 2004
"Ben Hinkle" <bhinkle4 juno.com> wrote in message news:btd7ks$1li5$1 digitaldaemon.com...I agree it would probably be better to have binary overloading decoupled from methods (trading off run-time dispatching against extending classes without subclassing). But... from the D doc: "In D, function overloading is simple. It matches exactly, it matcheswithimplicit conversions, or it does not match. If there is more than onematch,it is an error." So does that mean I can't specialize an existing function like: Number opAdd(Number, int) {...} NumberSubclass opAdd(NumberSubclass,int) {...} If I have a variable of type NumberSubclass and I write "x+1" then itcouldmatch both (one exactly and the other with an implicit conversion) and so that would be an error. Or what if I had a subclass of NumberSubclass (so there would be two implicit conversion matches).I sincerely hope that the compiler could umabiguously match the second operator. If not, we're storing up a heap of trouble for later. Walter, can you clarify what will happen here?It seems to me that this would make the proposed behavior of binaryoperatoroverloading less useful. Not unusable, I suppose. It would worth lookingata bunch of examples to see what would actually happen with either designandwhat the advantages and disadvantages of each would be. I for one can't immediately tell why one or the other would be a "no-brainer". -Ben "Antti Sykäri" <jsykari gamma.hut.fi> wrote in message news:slrnbvjcbm.69b.jsykari pulu.hut.fi...cannotIn article <btbv9t$2p2s$1 digitaldaemon.com>, Felix wrote:remember, thisMe too, I find a little weird the syntax Ta.opAdd(Tb) but, if Iis the standard way to overload operators in C++, too. Maybe, ifbesimply dropped, an alternative definition will be suitable.There's no "standard" way in C++, you can pick any way you like. The things that affect your choice are the same with operators and other functions: - only member functions/operators can be overridden in a derived class - only non-member functions can be used to extend the class after it has been closed For example, suppose that the standard output mechanism in D would be something like Printer stdout; void main() { stdout ~ "Hello world " ~ 1 ~ 2 ~ 3 ~ endl; } And Printer implemented something like: class Printer { Printer opCat(int i) { .. } Printer opCat(char[] c) { .. } Printer opCat(Object o) { .. } } [replace Printer with OutputStream if you feel very generic] then the only way to introduce a new printable user-defined type, say Vector3, that isn't derived from Object but still can be printed to stdout is to make a free-standing function: Printer opCat(Printer p, Vector3 v) { p ~ "(" ~ v.x ~ ", " ~ v.y ~ ", " ~ v.z ~ ")"; } That is, if that was possible in D. You can do that in C++. If D was an OO only language, then making types not derived from Object would not be possible and the issue would be moot. Heck, we could be as well programming in Java. But I gather D is supposed be multiparadigm and all that, and you ought to be able to do stuff also without objects if you want. I object to objects. And if there is some template code somewhere, saying "stdout ~ t" where t is of type which is argument for the template, you sure as hell want your Vector3 to be printed instead of getting a message along the lines of "Printer does not have member function opCat(Printer, Vector3). Wanna change it? You can't! Bwahaha!". Duh! So to conclude the rant, I fully agree with davepermen:just get rid of it. binary operators don't belong to a member. they never did.and Matthew:That's something we need that is supported by a large number of issues, not just this one. It's an absolute must, and I can't seee Walter escaping us on this one. :)The only potential problems I see with free-standing operators are: 1. Importing or not importing a module containing operators will affect the way functions are overloaded. "a + b" might mean a different thing after adding an import. On the other hand, normal function calls are already vulnerable for this and I haven't experienced any problems whatsoever. Nor have I had any problems with the issue in C++, where it also exists. 2. The overloading rules might be a tad more complex with freestanding operators. That is, they might require some thinking so that they don't cause any surprises. Maybe there is someone less tired to perform the thinking ;) Complexity doesn't bother me if the rules are sufficiently simple to grasp intuitively. (Unless they end up in some Snake-Tongued Two-Phased Koenig Lookup or similar maze of mirrors) If there are any other valid reasons against non-member operators, please bring them forth because I for one have forgotten them. Did the original D "vision" include them? Why/why not? Is the reason that they haven't been implemented a reason of principle or merely a practical one? By tackling those issues one by one we might even arrive at a solution that satisfies everyone. At least, one can hope... -Antti
Jan 05 2004
"Matthew" <matthew.hat stlsoft.dot.org> wrote in message news:btd95n$1o1a$1 digitaldaemon.com..."Ben Hinkle" <bhinkle4 juno.com> wrote in message news:btd7ks$1li5$1 digitaldaemon.com...soI agree it would probably be better to have binary overloading decoupled from methods (trading off run-time dispatching against extending classes without subclassing). But... from the D doc: "In D, function overloading is simple. It matches exactly, it matcheswithimplicit conversions, or it does not match. If there is more than onematch,it is an error." So does that mean I can't specialize an existing function like: Number opAdd(Number, int) {...} NumberSubclass opAdd(NumberSubclass,int) {...} If I have a variable of type NumberSubclass and I write "x+1" then itcouldmatch both (one exactly and the other with an implicit conversion) and(sothat would be an error. Or what if I had a subclass of NumberSubclassIt'll match the second, since it is an exact match. The first is a match with conversions.there would be two implicit conversion matches).I sincerely hope that the compiler could umabiguously match the second operator. If not, we're storing up a heap of trouble for later. Walter, can you clarify what will happen here?
Jan 06 2004
"Antti Sykäri" <jsykari gamma.hut.fi> wrote in message news:slrnbvjcbm.69b.jsykari pulu.hut.fi...If there are any other valid reasons against non-member operators, please bring them forth because I for one have forgotten them. Did the original D "vision" include them? Why/why not? Is the reason that they haven't been implemented a reason of principle or merely a practical one?The problem is that if they were implemented, then there'd be a need for Koenig lookup. Once there's Koenig lookup, then there's the export lookup madness.
Jan 05 2004
"Walter" <walter digitalmars.com> wrote in message news:btda5i$1puo$1 digitaldaemon.com..."Antti Sykäri" <jsykari gamma.hut.fi> wrote in message news:slrnbvjcbm.69b.jsykari pulu.hut.fi...With you so farIf there are any other valid reasons against non-member operators, please bring them forth because I for one have forgotten them. Did the original D "vision" include them? Why/why not? Is the reason that they haven't been implemented a reason of principle or merely a practical one?The problem is that if they were implemented, then there'd be a need for Koenig lookup.Once there's Koenig lookup, then there's the export lookup madness.Please explain
Jan 05 2004
"Matthew" <matthew.hat stlsoft.dot.org> wrote in message news:btdalv$1qml$1 digitaldaemon.com..."Walter" <walter digitalmars.com> wrote in message news:btda5i$1puo$1 digitaldaemon.com...I've done some extensive googling for info on export. It seems the main difficulty with export is the ADL. The two phase lookup comes in to play, once when the template is defined and the other when it is instantiated. The second lookup is the ADL one. The ADL can cut across all of the 'translation units', meaning an arbitrarilly large number of separate symbol tables need to be analyzed. This is essentially madness. As best as I can tell, this problem was not realized when export was voted into the Standard, and is the source of many opinions that export is unimplementable. EDG proved it could be implemented, reportedly consuming 3 man years, but I don't hear any more about the alleged advantages of export (code hiding, faster compilation), and hence I suspect those advantages do not occur in practice. Export is a canonical example of how backwards compatibility with seemingly innocuous design decisions can lead to disaster. I do not wish to import that madness into D <g>."Antti Sykäri" <jsykari gamma.hut.fi> wrote in message news:slrnbvjcbm.69b.jsykari pulu.hut.fi...With you so farIf there are any other valid reasons against non-member operators, please bring them forth because I for one have forgotten them. Did the original D "vision" include them? Why/why not? Is the reason that they haven't been implemented a reason of principle or merely a practical one?The problem is that if they were implemented, then there'd be a need for Koenig lookup.Once there's Koenig lookup, then there's the export lookup madness.Please explain
Jan 06 2004
"Matthew" <matthew.hat stlsoft.dot.org> wrote in message news:btdalv$1qml$1 digitaldaemon.com...the"Walter" <walter digitalmars.com> wrote in message news:btda5i$1puo$1 digitaldaemon.com..."Antti Sykäri" <jsykari gamma.hut.fi> wrote in message news:slrnbvjcbm.69b.jsykari pulu.hut.fi...If there are any other valid reasons against non-member operators, please bring them forth because I for one have forgotten them. Didtheyoriginal D "vision" include them? Why/why not? Is the reason thatforhaven't been implemented a reason of principle or merely a practical one?The problem is that if they were implemented, then there'd be a needTheI've done some extensive googling for info on export. It seems the main difficulty with export is the ADL. The two phase lookup comes in to play, once when the template is defined and the other when it is instantiated.Koenig lookup.With you so farOnce there's Koenig lookup, then there's the export lookup madness.Please explainsecond lookup is the ADL one. The ADL can cut across all of the'translationunits', meaning an arbitrarilly large number of separate symbol tablesneedto be analyzed. This is essentially madness. As best as I can tell, this problem was not realized when export was voted into the Standard, and is the source of many opinions that export is unimplementable. EDG proved it could be implemented, reportedly consuming3man years, but I don't hear any more about the alleged advantages ofexport(code hiding, faster compilation), and hence I suspect those advantages do not occur in practice. Export is a canonical example of how backwards compatibility withseeminglyinnocuous design decisions can lead to disaster. I do not wish to import that madness into D <g>.I've not given it enough thought on a global scale, but I can't see how you can expect to get away without ADL, with functions and non-member operators. If this means you must go for export madness, then you'd better get out the white coat.
Jan 06 2004
"Matthew" <matthew.hat stlsoft.dot.org> wrote in message news:bte1ib$2v71$1 digitaldaemon.com...I've not given it enough thought on a global scale, but I can't see howyoucan expect to get away without ADL, with functions and non-memberoperators. ADL is justified by non-member operators. Without non-member operators, no ADL is needed.If this means you must go for export madness, then you'd better get outthewhite coat.
Jan 06 2004
"Walter" <walter digitalmars.com> wrote in message news:bte342$31cg$1 digitaldaemon.com..."Matthew" <matthew.hat stlsoft.dot.org> wrote in message news:bte1ib$2v71$1 digitaldaemon.com...Not true. Generalising shims require ADL for a kick off.I've not given it enough thought on a global scale, but I can't see howyoucan expect to get away without ADL, with functions and non-memberoperators. ADL is justified by non-member operators. Without non-member operators, no ADL is needed.
Jan 06 2004
"Matthew" <matthew.hat stlsoft.dot.org> wrote in message news:bteaea$akr$1 digitaldaemon.com...Not true. Generalising shims require ADL for a kick off.Example?
Jan 06 2004
dunno.. i thought thats only for namespaces.. In article <btda5i$1puo$1 digitaldaemon.com>, Walter says..."Antti Sykäri" <jsykari gamma.hut.fi> wrote in message news:slrnbvjcbm.69b.jsykari pulu.hut.fi...If there are any other valid reasons against non-member operators, please bring them forth because I for one have forgotten them. Did the original D "vision" include them? Why/why not? Is the reason that they haven't been implemented a reason of principle or merely a practical one?The problem is that if they were implemented, then there'd be a need for Koenig lookup. Once there's Koenig lookup, then there's the export lookup madness.
Jan 06 2004
i don't see how the operator overloading can result in more work to "look up".. if the parser finds some code snipped like this: a + b then it simply translates it to opAdd(a,b) directly. and then its just a normal function call.. and done. the normal operator rules for encoding/parsing code-lines with operators still apply, and the endresult is pleasing, and with no issues in parsing at all.. class Printer { Printer print(char[] text) { ...; return this; } } Printer opStream(Printer p,char[] text) { return p.print(text); } Printer opStream(Printer p,int value) { return p.print(toString(value)); } say opStream is <~ (my lovely token:D).. then you can write Printer opStream(Printer p,vec3 v) { p <~ '(' <~ x <~ ',' <~ y <~ ',' <~ z <~ ')'; } and a simple piece of code would be like this: Printer p(stdout); p <~ "Position: " <~ position; wich would translate to opStream(opStream(p,"Position: "),position); and would then simply decode to opStream(opStream(Printer,char[]),vec3); for now, it would be doable with opCall.. (as yet suggested by some). opStream could get some syntactic sugar, like we don't need to write StreamerType opStream(StreamerType s,T) { /* access stream */ return s; } but only char[] opStream(T) { /* access stream */ return streamlined data packet; // doesn't have to be char[], but in case of io, you stream with char[] often } and class StreamerType { opStream(char[] s) { /* print stream */ } } example: class Printer { opStream(char[] s) { printf("%.*s",s); } } char[] opStream(char[] text) { return text; // just rewrite it to the stream as its the base format. } char[] opStream(vec3) { return <~'('<~x<~','<~y<~','<~z<~')'; } Printer p; p <~ "Position: " <~ position; would translate to p.opStream(opStream(text) ~ opStream(position)); this thought is not finished, but merely an idea.. but overloadable non-member operators are ESSENCIAL. and i don't see why they are, at all, different, than what we have now. technically. In article <slrnbvjcbm.69b.jsykari pulu.hut.fi>, Antti =?iso-8859-1?Q?Syk=E4ri?= says...In article <btbv9t$2p2s$1 digitaldaemon.com>, Felix wrote:Me too, I find a little weird the syntax Ta.opAdd(Tb) but, if I remember, this is the standard way to overload operators in C++, too. Maybe, if cannot be simply dropped, an alternative definition will be suitable.There's no "standard" way in C++, you can pick any way you like. The things that affect your choice are the same with operators and other functions: - only member functions/operators can be overridden in a derived class - only non-member functions can be used to extend the class after it has been closed For example, suppose that the standard output mechanism in D would be something like Printer stdout; void main() { stdout ~ "Hello world " ~ 1 ~ 2 ~ 3 ~ endl; } And Printer implemented something like: class Printer { Printer opCat(int i) { .. } Printer opCat(char[] c) { .. } Printer opCat(Object o) { .. } } [replace Printer with OutputStream if you feel very generic] then the only way to introduce a new printable user-defined type, say Vector3, that isn't derived from Object but still can be printed to stdout is to make a free-standing function: Printer opCat(Printer p, Vector3 v) { p ~ "(" ~ v.x ~ ", " ~ v.y ~ ", " ~ v.z ~ ")"; } That is, if that was possible in D. You can do that in C++. If D was an OO only language, then making types not derived from Object would not be possible and the issue would be moot. Heck, we could be as well programming in Java. But I gather D is supposed be multiparadigm and all that, and you ought to be able to do stuff also without objects if you want. I object to objects. And if there is some template code somewhere, saying "stdout ~ t" where t is of type which is argument for the template, you sure as hell want your Vector3 to be printed instead of getting a message along the lines of "Printer does not have member function opCat(Printer, Vector3). Wanna change it? You can't! Bwahaha!". Duh! So to conclude the rant, I fully agree with davepermen:just get rid of it. binary operators don't belong to a member. they never did.and Matthew:That's something we need that is supported by a large number of issues, not just this one. It's an absolute must, and I can't seee Walter escaping us on this one. :)The only potential problems I see with free-standing operators are: 1. Importing or not importing a module containing operators will affect the way functions are overloaded. "a + b" might mean a different thing after adding an import. On the other hand, normal function calls are already vulnerable for this and I haven't experienced any problems whatsoever. Nor have I had any problems with the issue in C++, where it also exists. 2. The overloading rules might be a tad more complex with freestanding operators. That is, they might require some thinking so that they don't cause any surprises. Maybe there is someone less tired to perform the thinking ;) Complexity doesn't bother me if the rules are sufficiently simple to grasp intuitively. (Unless they end up in some Snake-Tongued Two-Phased Koenig Lookup or similar maze of mirrors) If there are any other valid reasons against non-member operators, please bring them forth because I for one have forgotten them. Did the original D "vision" include them? Why/why not? Is the reason that they haven't been implemented a reason of principle or merely a practical one? By tackling those issues one by one we might even arrive at a solution that satisfies everyone. At least, one can hope... -Antti
Jan 06 2004
"davepermen" <davepermen_member pathlink.com> wrote in message news:bte3p3$pp$1 digitaldaemon.com...i don't see how the operator overloading can result in more work to "lookup"..if the parser finds some code snipped like this: a + b then it simply translates it to opAdd(a,b) directly. and then its just a normal function call.. and done. the normal operator rules for encoding/parsing code-lines with operators still apply,andthe endresult is pleasing, and with no issues in parsing at all..The problem is if opAdd() is defined in another module that is not the current module. It will not be found with the normal scoped lookup. With ordinary functions, one just does: foo.opAdd(a,b) if the function is in module foo. That doesn't work out so good for operator overloads. What ADL does is look at the types of the arguments to the function, and uses those types to add more scopes to look for the opAdd in.
Jan 06 2004
Walter wrote:The problem is if opAdd() is defined in another module that is not the current module. It will not be found with the normal scoped lookup. With ordinary functions, one just does: foo.opAdd(a,b) if the function is in module foo. That doesn't work out so good for operator overloads. What ADL does is look at the types of the arguments to the function, and uses those types to add more scopes to look for the opAdd in.Would that even be sufficient? Doesn't one expect to be able to write an opAdd that works on objects whose classes are defined in other modules? I.e. what if the opAdd is in neither of the object modules, but in a completely different one? Seems to me that the only sensible solution is to require that the module opAdd is defined in has to be imported for it to be used. Apply the same rules as in overloaded function calling - it IS only syntactic sugar, after all. import math.vectors; import gnu.bignum; import bigNumIntegration; //defines opAdd(Vector,BigNum) Vector v; BigNum b; v + b; //works only if fancyOperatorModule is imported And if a compatible operator is defined in more than one imported module then a qualified call of the type bigNumIntegration.opAdd(v,b) instead of (v + b) can be used. Those cases should be rare, so no big deal. Hauke
Jan 06 2004
Hauke Duden wrote:v + b; //works only if fancyOperatorModule is importedSorry, that should have been v + b; //works only if bigNumIntegration is imported
Jan 06 2004
Walter wrote:operatorThe problem is if opAdd() is defined in another module that is not the current module. It will not be found with the normal scoped lookup. With ordinary functions, one just does: foo.opAdd(a,b) if the function is in module foo. That doesn't work out so good forin.overloads. What ADL does is look at the types of the arguments to the function, and uses those types to add more scopes to look for the opAddWould that even be sufficient? Doesn't one expect to be able to write an opAdd that works on objects whose classes are defined in other modules?Absolutely. And without which, we'll all be hitting ourselves in the head and turning for the C++ compiler.I.e. what if the opAdd is in neither of the object modules, but in a completely different one? Seems to me that the only sensible solution is to require that the module opAdd is defined in has to be imported for it to be used. Apply the same rules as in overloaded function calling - it IS only syntactic sugar, after all.But what about when you're working with permutations of all kinds of types from potentially unlimited modules, in generic code? D wants to be generic. Therefore D needs ADL. If ADL is hard for compilers, so be it. Those appear to be the breaks.
Jan 06 2004
Matthew wrote:I have no idea what ADL is, but wouldn't the same problem also apply to all other overloaded global functions? I think maybe I understand the problem now. Please correct me if I'm wrong: If you write a template that contains a function call to foo(A a,B b), where A and B are template parameters, then this template will be instantiated only once for each combination of (A,B) it is used with. The problem is that if you want to overload foo in a module unrelated to A and B, then the template instance becomes dependent not only on the types A and B, but also on the module where foo is defined. But THAT may be different, depending on where the template is actually used, since different places of usage may import different modules. I do not see how this problem can ever be solved, except by treating templates like macros and simply always compiling it in the context it is used in, every time. But if this isn't solved for "normal" global functions, why does it have to be solved for global operators? Besides, I think the problem should be pretty rare in practice. The only situation where it would cause trouble is if the same template is used with the same two types at two different places in the program, and you have imported a different definition of the operator/global function in one context than in the other. In the case of stream operators and stuff like that, I think that you'll usually have at most one overload for each combination of types. So, can't the calls of global functions/operators inside templates be resolved from the context where the template is instantiated first? That seems to be the only workable alternative to me. HaukeI.e. what if the opAdd is in neither of the object modules, but in a completely different one? Seems to me that the only sensible solution is to require that the module opAdd is defined in has to be imported for it to be used. Apply the same rules as in overloaded function calling - it IS only syntactic sugar, after all.But what about when you're working with permutations of all kinds of types from potentially unlimited modules, in generic code? D wants to be generic. Therefore D needs ADL. If ADL is hard for compilers, so be it. Those appear to be the breaks.
Jan 06 2004
"Hauke Duden" <H.NS.Duden gmx.net> wrote in message news:bte5b4$3cq$1 digitaldaemon.com...Walter wrote:operatorThe problem is if opAdd() is defined in another module that is not the current module. It will not be found with the normal scoped lookup. With ordinary functions, one just does: foo.opAdd(a,b) if the function is in module foo. That doesn't work out so good forin.overloads. What ADL does is look at the types of the arguments to the function, and uses those types to add more scopes to look for the opAddWould that even be sufficient? Doesn't one expect to be able to write an opAdd that works on objects whose classes are defined in other modules?Can't the function dispatch a method call by hand? For example Number opAdd(Number a, Number2 b) { return b.opAdd(a); } Then it is up to Number2 (or a subclass of Number2) to define opAdd. It would be nice to have the compiler's ability to detect if a method exists of a given signature before calling it, though, which would mean some reflection support. I would definitely want to keep all function resolution to things in scope and leave unscoped function resolution to run-time dispatching. That seems the only sane way to go.I.e. what if the opAdd is in neither of the object modules, but in a completely different one? Seems to me that the only sensible solution is to require that the module opAdd is defined in has to be imported for it to be used. Apply the same rules as in overloaded function calling - it IS only syntactic sugar, after all. import math.vectors; import gnu.bignum; import bigNumIntegration; //defines opAdd(Vector,BigNum) Vector v; BigNum b; v + b; //works only if fancyOperatorModule is imported And if a compatible operator is defined in more than one imported module then a qualified call of the type bigNumIntegration.opAdd(v,b) instead of (v + b) can be used. Those cases should be rare, so no big deal. Hauke
Jan 06 2004
"Hauke Duden" <H.NS.Duden gmx.net> wrote in message news:bte5b4$3cq$1 digitaldaemon.com...Walter wrote:operatorThe problem is if opAdd() is defined in another module that is not the current module. It will not be found with the normal scoped lookup. With ordinary functions, one just does: foo.opAdd(a,b) if the function is in module foo. That doesn't work out so good forin.overloads. What ADL does is look at the types of the arguments to the function, and uses those types to add more scopes to look for the opAddWould that even be sufficient? Doesn't one expect to be able to write an opAdd that works on objects whose classes are defined in other modules?Of course. But how member operator lookup works is: a + b If a is an instance of Foo, then Foo.opAdd(b) is looked for. Foo can be in another module. No special ADL rule is required.
Jan 06 2004
"Walter" <walter digitalmars.com> wrote in message news:btf3no$1hk9$2 digitaldaemon.com... | | | Of course. But how member operator lookup works is: | | a + b | | If a is an instance of Foo, then Foo.opAdd(b) is looked for. Foo can be in | another module. No special ADL rule is required. | | What if all operators can be reversed? That way, if there's no Foo.opAdd(typeof(b)), the compiler will look for typeof(b).opAdd_r(Foo). Also, those who want to possibly extend a stream class, would do MyType.opAdd_r(Stream). What about that? ----------------------- Carlos Santander Bernal
Jan 06 2004
"Carlos Santander B." <carlos8294 msn.com> wrote in message news:btfh6d$26rc$1 digitaldaemon.com..."Walter" <walter digitalmars.com> wrote in message news:btf3no$1hk9$2 digitaldaemon.com... | | | Of course. But how member operator lookup works is: | | a + b | | If a is an instance of Foo, then Foo.opAdd(b) is looked for. Foo can bein| another module. No special ADL rule is required. | | What if all operators can be reversed? That way, if there's no Foo.opAdd(typeof(b)), the compiler will look for typeof(b).opAdd_r(Foo).Since add is commutative, the compiler will first look for typeof(a).opAdd, if that isn't found, it will look for typeof(b).opAdd.Also, those who want to possibly extend a stream class, would do MyType.opAdd_r(Stream). What about that?I philosophically object to using operator overloading for stream I/O <g>.
Jan 06 2004
what bether thing do you suggest then? with the operators, we get a nice simple syntax to the way it has to be: for every type-part of the stream, we have an "addObjToStream" function that gets called, and does exactly the needed work. D is compile time typed, as is c++. so everything about the type determination should be compile time. only the needed part, a.k.a. the actual conversion of a type to the streamformat, should be runtime (if needed:D). and a direct function per type (and streamtype) would work perfect. not any more runtime overhead than needed. but write(stream,"Hello, my name is "); write(stream,name); is clumpsy (while exactly doable currently that way). and that is why operator overloading would fit in that perfectly. it makes it possible to chain a lot of such write calls into one rather easy to read line. if you can think logically, you would not try to judge phylosophically about it. c++ provides a solution that is typesave, very simple extendable, working with existing language features, is simple, and can be very fast. the c++ version possibly isn't. but the D version can. In article <btfjb9$2a4k$1 digitaldaemon.com>, Walter says..."Carlos Santander B." <carlos8294 msn.com> wrote in message news:btfh6d$26rc$1 digitaldaemon.com..."Walter" <walter digitalmars.com> wrote in message news:btf3no$1hk9$2 digitaldaemon.com... | | | Of course. But how member operator lookup works is: | | a + b | | If a is an instance of Foo, then Foo.opAdd(b) is looked for. Foo can bein| another module. No special ADL rule is required. | | What if all operators can be reversed? That way, if there's no Foo.opAdd(typeof(b)), the compiler will look for typeof(b).opAdd_r(Foo).Since add is commutative, the compiler will first look for typeof(a).opAdd, if that isn't found, it will look for typeof(b).opAdd.Also, those who want to possibly extend a stream class, would do MyType.opAdd_r(Stream). What about that?I philosophically object to using operator overloading for stream I/O <g>.
Jan 07 2004
"davepermen" <davepermen_member pathlink.com> wrote in message news:btgjga$skd$1 digitaldaemon.com...what bether thing do you suggest then? with the operators, we get a nicesimplesyntax to the way it has to be: for every type-part of the stream, we havean"addObjToStream" function that gets called, and does exactly the neededwork. There was an earlier thread about overloading () to implement stream I/O, it works nicely and does not require ADL. It winds up looking like: stdout(arg1)(arg2)(arg3) instead of (in C++): stdout << arg1 << arg2 << arg3 and besides, ADL isn't necessary for the latter either if << is overloaded as a member of stdout.
Jan 07 2004
i do understand that. but you simply can't overload your prefered operator for ANY POSSIBLE TYPE EVER EXISTING. this is just not possible. free overloadable functions give the ability to add overloads to your stream without any rewriting of stream or type. THAT is why i want free operators. they are the way operators behave, and they are the only way to make streams fast extendable and type save. and FAST. everyone cries that they have to be faster than the c++ streams. believe me they can be hell fast. and i can for sure overload all sort of types for my stdout class and then write stdout << a << b << c; but this is nost scalable. a library ALWAYS has to provide features a user can use for ANY situation. that means he has to have a nice plugin-interface. overloadable free operators are a GREAT plugin-interface for streams. the by far best. In article <btglpg$100p$1 digitaldaemon.com>, Walter says..."davepermen" <davepermen_member pathlink.com> wrote in message news:btgjga$skd$1 digitaldaemon.com...what bether thing do you suggest then? with the operators, we get a nicesimplesyntax to the way it has to be: for every type-part of the stream, we havean"addObjToStream" function that gets called, and does exactly the neededwork. There was an earlier thread about overloading () to implement stream I/O, it works nicely and does not require ADL. It winds up looking like: stdout(arg1)(arg2)(arg3) instead of (in C++): stdout << arg1 << arg2 << arg3 and besides, ADL isn't necessary for the latter either if << is overloaded as a member of stdout.
Jan 07 2004
"davepermen" <davepermen_member pathlink.com> wrote in message news:bthndh$2jdb$1 digitaldaemon.com...i do understand that. but you simply can't overload your prefered operatorforANY POSSIBLE TYPE EVER EXISTING. this is just not possible. free overloadable functions give the ability to add overloads to yourstreamwithout any rewriting of stream or type. THAT is why i want free operators. they are the way operators behave, andtheyare the only way to make streams fast extendable and type save. and FAST. everyone cries that they have to be faster than the c++ streams. believeme theycan be hell fast. and i can for sure overload all sort of types for mystdoutclass and then write stdout << a << b << c; but this is nost scalable. a library ALWAYS has to provide features a user can use for ANY situation.thatmeans he has to have a nice plugin-interface. overloadable free operatorsare aGREAT plugin-interface for streams.I understand your point. But there's got to be a better way than ADL.
Jan 09 2004
operators have to be in the module you use them (sort of private operators), or in one of the modules of one of the used types. just as overloadable functions, more or less.. on the other hand.. the print(..) { /+ defines this is chainable +/ } // only two dots, like in [x..y] print(int x) { printf("%i",x); } print(char[] x) { printf("%.*s",x); } print(vec3 x) { printf("(%f,%f,%f)",x.x,x.y,x.z); } etc wich concatenates print("Hello World, my Name is ",davepermen," I'm ",19," and i life at ",position); wich will then map to print(char[]); // print("Hello World, my Name is "); print(char[]); // print(davepermen); print(char[]); // print(" I'm "); print(int); // print(19); print(char[]); // print(" and i life at "); print(vec3); // print(position); and if this is too implicit, possibly we have to call the function more like this: print..("Hello World, my Name is ",davepermen," I'm ",19," and i life at ",position); // note the two dots.. or even print("Hello World, my Name is "..davepermen.." I'm "..19.." and i life at "..position); // hm, a real stream-chain operator how to mix that with oo? dunno.. :| anyways.. lot to think about.. how to make the language flexible extendable without issues for the compiler? one word, walter: shit :D at least you see my points as well.. now lets find some way to work this out. and then, syntax-sugar it as much as possible:D In article <btlq6k$2obr$1 digitaldaemon.com>, Walter says..."davepermen" <davepermen_member pathlink.com> wrote in message news:bthndh$2jdb$1 digitaldaemon.com...i do understand that. but you simply can't overload your prefered operatorforANY POSSIBLE TYPE EVER EXISTING. this is just not possible. free overloadable functions give the ability to add overloads to yourstreamwithout any rewriting of stream or type. THAT is why i want free operators. they are the way operators behave, andtheyare the only way to make streams fast extendable and type save. and FAST. everyone cries that they have to be faster than the c++ streams. believeme theycan be hell fast. and i can for sure overload all sort of types for mystdoutclass and then write stdout << a << b << c; but this is nost scalable. a library ALWAYS has to provide features a user can use for ANY situation.thatmeans he has to have a nice plugin-interface. overloadable free operatorsare aGREAT plugin-interface for streams.I understand your point. But there's got to be a better way than ADL.
Jan 09 2004
Streams are another kind of iterator (input, or output). It's also another one of those situations that must be extended to any type, and where the code that controls the iteration (exactly what to print) is typically written well after the code that deals with each iteration (how to print each kind of thing) which is written well after the buffering code. But the later code can't be fast unless the buffering code is spread out throughout the outer function calls (heavily inlined to the point where the generated code is just writing bytes directly where they go in the stream buffer, advancing the buffer pointer or calling to allocate more memory periodically). This is hard. It needs some kind of code injection that you can do in C++ by class wrapping, but not so easily in D (no real dtors, ill-defined order of shutdown, etc). Anyway we need to be able to extend it in quite a few different ways. We need to be able to add new types that can be input or output, or saved and loaded, we need to be able to add new types that can act as streams, and hopefully integrate with the container classes somehow to the point where you could say: sys.outstream s; int c[100]; s.print(c[]); or, better, something like: s.print(intersperse(c[], ',')); Sean "davepermen" <davepermen_member pathlink.com> wrote in message news:btlt7b$2t6n$1 digitaldaemon.com...operators have to be in the module you use them (sort of privateoperators), orin one of the modules of one of the used types. just as overloadablefunctions,more or less.. on the other hand.. the print(..) { /+ defines this is chainable +/ } // only two dots, like in[x..y]print(int x) { printf("%i",x); } print(char[] x) { printf("%.*s",x); } print(vec3 x) { printf("(%f,%f,%f)",x.x,x.y,x.z); } etc wich concatenates print("Hello World, my Name is ",davepermen," I'm ",19," and i life at ",position); wich will then map to print(char[]); // print("Hello World, my Name is "); print(char[]); // print(davepermen); print(char[]); // print(" I'm "); print(int); // print(19); print(char[]); // print(" and i life at "); print(vec3); // print(position); and if this is too implicit, possibly we have to call the function morelikethis: print..("Hello World, my Name is ",davepermen," I'm ",19," and i life at ",position); // note the two dots.. or even print("Hello World, my Name is "..davepermen.." I'm "..19.." and i life at "..position); // hm, a real stream-chain operator how to mix that with oo? dunno.. :| anyways.. lot to think about.. how to make the language flexibleextendablewithout issues for the compiler? one word, walter: shit :D at least you see my points as well.. now lets find some way to workthis out.and then, syntax-sugar it as much as possible:D In article <btlq6k$2obr$1 digitaldaemon.com>, Walter says...operator"davepermen" <davepermen_member pathlink.com> wrote in message news:bthndh$2jdb$1 digitaldaemon.com...i do understand that. but you simply can't overload your preferedandforANY POSSIBLE TYPE EVER EXISTING. this is just not possible. free overloadable functions give the ability to add overloads to yourstreamwithout any rewriting of stream or type. THAT is why i want free operators. they are the way operators behave,FAST.theyare the only way to make streams fast extendable and type save. andbelieveeveryone cries that they have to be faster than the c++ streams.situation.me theycan be hell fast. and i can for sure overload all sort of types for mystdoutclass and then write stdout << a << b << c; but this is nost scalable. a library ALWAYS has to provide features a user can use for ANYoperatorsthatmeans he has to have a nice plugin-interface. overloadable freeare aGREAT plugin-interface for streams.I understand your point. But there's got to be a better way than ADL.
Jan 11 2004
davepermen wrote:what bether thing do you suggest then? with the operators, we get a nice simple syntax to the way it has to be: for every type-part of the stream, we have an "addObjToStream" function that gets called, and does exactly the needed work. D is compile time typed, as is c++. so everything about the type determination should be compile time. only the needed part, a.k.a. the actual conversion of a type to the streamformat, should be runtime (if needed:D). and a direct function per type (and streamtype) would work perfect. not any more runtime overhead than needed. but write(stream,"Hello, my name is "); write(stream,name); is clumpsy (while exactly doable currently that way). and that is why operator overloading would fit in that perfectly. it makes it possible to chain a lot of such write calls into one rather easy to read line. if you can think logically, you would not try to judge phylosophically about it. c++ provides a solution that is typesave, very simple extendable, working with existing language features, is simple, and can be very fast. the c++ version possibly isn't. but the D version can.It's an irk. I don't see any way to work around it within the confines of D without resorting to polymorphism. The question is merely whether polymorphism is a good way to deal with this. Given that we're talking about something that would (certainly should) be in the standard library, I don't see why not. Making streams deal with PDTs is trivial enough, since they are defined by the compiler, not the code. Dealing with objects is similarly easy: define a Streamable interface, or just use Object.toString(). Structs require a few tiny hoops, but even that's pretty easy: class StreamWrap(T) : IStreamable { this(T t) { _t = t; } void toStream(Stream stream) { stream.write(_t.toStream()); } } The template could further be specialized for reference types and PDTs to provide genericity. Free operator functions would be nice, but it doesn't seem to be the only good way to handle the streaming issue. -- andy
Jan 07 2004
but the by far most straightforward and logical way. you don't need delegates as well, you can do that with a library-implementation. see boost::function. the trick is, with operator overloading as free functions we get much more flexibility than only for streams. but there its most obvious. it will be as fast as directly setting up the specific code for your specific output stream format manually. (the chain of write(stream,T) calls), wich is the fastest way. in every other situation, we have to rely on compiler optimisations to _KNOW_ its as fast. if you don't see that, you don't see encapsulation as something important, then? because operators don't belong to types, they should never be in their interface. In article <bthkn2$2eq0$1 digitaldaemon.com>, Andy Friesen says...davepermen wrote:what bether thing do you suggest then? with the operators, we get a nice simple syntax to the way it has to be: for every type-part of the stream, we have an "addObjToStream" function that gets called, and does exactly the needed work. D is compile time typed, as is c++. so everything about the type determination should be compile time. only the needed part, a.k.a. the actual conversion of a type to the streamformat, should be runtime (if needed:D). and a direct function per type (and streamtype) would work perfect. not any more runtime overhead than needed. but write(stream,"Hello, my name is "); write(stream,name); is clumpsy (while exactly doable currently that way). and that is why operator overloading would fit in that perfectly. it makes it possible to chain a lot of such write calls into one rather easy to read line. if you can think logically, you would not try to judge phylosophically about it. c++ provides a solution that is typesave, very simple extendable, working with existing language features, is simple, and can be very fast. the c++ version possibly isn't. but the D version can.It's an irk. I don't see any way to work around it within the confines of D without resorting to polymorphism. The question is merely whether polymorphism is a good way to deal with this. Given that we're talking about something that would (certainly should) be in the standard library, I don't see why not. Making streams deal with PDTs is trivial enough, since they are defined by the compiler, not the code. Dealing with objects is similarly easy: define a Streamable interface, or just use Object.toString(). Structs require a few tiny hoops, but even that's pretty easy: class StreamWrap(T) : IStreamable { this(T t) { _t = t; } void toStream(Stream stream) { stream.write(_t.toStream()); } } The template could further be specialized for reference types and PDTs to provide genericity. Free operator functions would be nice, but it doesn't seem to be the only good way to handle the streaming issue. -- andy
Jan 07 2004
davepermen wrote:but the by far most straightforward and logical way. you don't need delegates as well, you can do that with a library-implementation. see boost::function.There are lots of reasons not to use boost. It's big and monolithic and doesn't nicely fit into other build systems. Blech. boost::function has an inordinately convoluted implementation as well. (which includes some impressively obfuscated preprocessor hackery, and just plain brute forced template declarations for dealing with up to 50 arguments)the trick is, with operator overloading as free functions we get much more flexibility than only for streams. but there its most obvious. it will be as fast as directly setting up the specific code for your specific output stream format manually. (the chain of write(stream,T) calls), wich is the fastest way. in every other situation, we have to rely on compiler optimisations to _KNOW_ its as fast. if you don't see that, you don't see encapsulation as something important, then? because operators don't belong to types, they should never be in their interface.I'd like to see proof that one more virtual call per nonPDT element being written is going to be unaccetably slow before agreeing that it's the only way. Premature optimization and all that. Fast is good, but one of the main goals of D is to keep Walter from having to turn his head inside out making the compiler work right. Simple compilers also tend to be fast compilers. I'm willing to give a few inches on this subject. As for encapsulation, I do believe that nonmember functions are more encapsulated than member functions, since they are forced to rely on the public interface, and are therefore immune to implementation fluxuations. Operator overloads are a small enough minority that I don't see this as being a particularly important issue. There are far more methods that are not overloading an operator than are. -- andy
Jan 07 2004
davepermen wrote:this was sarcasm, dude! the hackery boost has to do to implement rather good features into c++ is impressive (boost itself is an amazing piece of code! nothing against that). but boost::function can not in any form replace a simple delegate as in Dbut the by far most straightforward and logical way. you don't need delegates as well, you can do that with a library-implementation. see boost::function.There are lots of reasons not to use boost. It's big and monolithic and doesn't nicely fit into other build systems. Blech. boost::function has an inordinately convoluted implementation as well. (which includes some impressively obfuscated preprocessor hackery, and just plain brute forced template declarations for dealing with up to 50 arguments)Fast is good, but one of the main goals of D is to keep Walter from having to turn his head inside out making the compiler work right. Simple compilers also tend to be fast compilers. I'm willing to give a few inches on this subject.fully agreeing with the compiler thing. but knowing there is a fast, and theoretically very simple way, to accomplish an in general very fast and proper solution by default, makes me believe its worth fighting for it. i see the issues, but it doesn't remove the fact, that the way c++ allows to write operators give us a great way to make it work without virtual functions.As for encapsulation, I do believe that nonmember functions are more encapsulated than member functions, since they are forced to rely on the public interface, and are therefore immune to implementation fluxuations.exactlyperator overloads are a small enough minority that I don't see this as being a particularly important issue. There are far more methods that are not overloading an operator than are.as they are just syntactic sugar, its, imho, entierly WRONG that they ever can access inner parts of an object.. but if its really not possible for walter to find a way he likes it, then its okay.. i still don't see any reason for it. operators and functions are in my eyes, 100% interchangeable. if you can't call a function without specifiyng the package, then you simply can't use such an operator, too.. wouldn't hurt ME at all. as they are just sugar, as i said, yet.
Jan 07 2004
<snip>if you don't see that, you don't see encapsulation as something important,then?because operators don't belong to types, they should never be in their interface.Well put!
Jan 07 2004
Hi, Using binary operators like '+', '*', etc. in mathematical expressions is elegant and desirable. But, for some algebras the multiplicative operation is not commutative. Is there a way to model alternative algebras (like geometric algebra) in D and still be able to make use of the syntactic sugar the operators provide? In particar, it would be desirable to turn of the commutative property of the '*' operator. Kind regards, Erik In article <btfjb9$2a4k$1 digitaldaemon.com>, Walter says..."Carlos Santander B." <carlos8294 msn.com> wrote in message news:btfh6d$26rc$1 digitaldaemon.com..."Walter" <walter digitalmars.com> wrote in message news:btf3no$1hk9$2 digitaldaemon.com... | | | Of course. But how member operator lookup works is: | | a + b | | If a is an instance of Foo, then Foo.opAdd(b) is looked for. Foo can bein| another module. No special ADL rule is required. | | What if all operators can be reversed? That way, if there's no Foo.opAdd(typeof(b)), the compiler will look for typeof(b).opAdd_r(Foo).Since add is commutative, the compiler will first look for typeof(a).opAdd, if that isn't found, it will look for typeof(b).opAdd.Also, those who want to possibly extend a stream class, would do MyType.opAdd_r(Stream). What about that?I philosophically object to using operator overloading for stream I/O <g>.
Jan 07 2004
And as well, the additive operation need not be commutative if you consider '+' to denote the binary operator of a monoid (for which the set of Java Strings with '+' as binary operator would be an example; http://mathworld.wolfram.com/Monoid.html) The commutativity of + and * is in the eye of the beholder. (By the way, while * in linear algebra is not commutative it is associative -- (A * B ) * C == A * (B * C) -- so maybe it would be best denoted by the behavior of the concatenation operator '~'! -- Just kidding.) -Antti In article <bthcg2$21eo$1 digitaldaemon.com>, Erik Baklund wrote:Hi, Using binary operators like '+', '*', etc. in mathematical expressions is elegant and desirable. But, for some algebras the multiplicative operation is not commutative. Is there a way to model alternative algebras (like geometric algebra) in D and still be able to make use of the syntactic sugar the operators provide? In particar, it would be desirable to turn of the commutative property of the '*' operator. Kind regards, Erik In article <btfjb9$2a4k$1 digitaldaemon.com>, Walter says..."Carlos Santander B." <carlos8294 msn.com> wrote in message news:btfh6d$26rc$1 digitaldaemon.com..."Walter" <walter digitalmars.com> wrote in message news:btf3no$1hk9$2 digitaldaemon.com... | | | Of course. But how member operator lookup works is: | | a + b | | If a is an instance of Foo, then Foo.opAdd(b) is looked for. Foo can bein| another module. No special ADL rule is required. | | What if all operators can be reversed? That way, if there's no Foo.opAdd(typeof(b)), the compiler will look for typeof(b).opAdd_r(Foo).Since add is commutative, the compiler will first look for typeof(a).opAdd, if that isn't found, it will look for typeof(b).opAdd.Also, those who want to possibly extend a stream class, would do MyType.opAdd_r(Stream). What about that?I philosophically object to using operator overloading for stream I/O <g>.
Jan 07 2004
this is great. for OWN TYPES. i'm still not able to add streaming capabilities to ANY type. types of libraries, types of a coworker, etc. you can't add streaming capability to some type without eighter change the stream class (just stupid), or the type class itself (just as stupid if its not your type). streaming capability (as any operators actually) physically, and phylosophically, don't belong to a type. they are an algorithm, working on 2 types, wich they don't belong to. they just use them. that is basic oo, that is basic encapsulation. you can read a lot about it on cuj.com if you want. don't make it a memberfunction if you don't need to. that is first rule of real oo and encapsulation. In article <btfh6d$26rc$1 digitaldaemon.com>, Carlos Santander B. says..."Walter" <walter digitalmars.com> wrote in message news:btf3no$1hk9$2 digitaldaemon.com... | | | Of course. But how member operator lookup works is: | | a + b | | If a is an instance of Foo, then Foo.opAdd(b) is looked for. Foo can be in | another module. No special ADL rule is required. | | What if all operators can be reversed? That way, if there's no Foo.opAdd(typeof(b)), the compiler will look for typeof(b).opAdd_r(Foo). Also, those who want to possibly extend a stream class, would do MyType.opAdd_r(Stream). What about that? ----------------------- Carlos Santander Bernal
Jan 07 2004
Walter wrote:Yes, I understand that. But I still don't see how this issue is different from having "normal" overloaded global functions. If I have two modules, foo and bar, each with a different version of function f, and I call f in another module, isn't that the exact same problem, as if f was an opAdd overload and the call an expression of the kind (a+b)? I really don't see how global operators are different from global functions! HaukeoperatorThe problem is if opAdd() is defined in another module that is not the current module. It will not be found with the normal scoped lookup. With ordinary functions, one just does: foo.opAdd(a,b) if the function is in module foo. That doesn't work out so good forin.overloads. What ADL does is look at the types of the arguments to the function, and uses those types to add more scopes to look for the opAddWould that even be sufficient? Doesn't one expect to be able to write an opAdd that works on objects whose classes are defined in other modules?Of course. But how member operator lookup works is: a + b If a is an instance of Foo, then Foo.opAdd(b) is looked for. Foo can be in another module. No special ADL rule is required.
Jan 07 2004
Hauke Duden wrote:Yes, I understand that. But I still don't see how this issue is different from having "normal" overloaded global functions.Read the manual. Overloads are only possible within one scope. That means, when defined on the module level, overloads only work correctly within that module. If defined within a class, only within a class. And so on.If I have two modules, foo and bar, each with a different version of function f, and I call f in another module, isn't that the exact same problem, as if f was an opAdd overload and the call an expression of the kind (a+b)?The problem is, it doesn't work.I really don't see how global operators are different from global functions!They are not. That's the reason cross-module overloads don't work. They don't work for normal functions. And for operators, it is simply bloody forbidden to use module scope, because it would not make the problem better, just worse! -eye
Jan 07 2004
Ilya Minkov wrote:Hauke Duden wrote:I wasn't aware of that - thanks for explaining. So the problem IS the same with global functions, and it is not solved there either, just circumvented. That doesn't make it any better, though :(. HaukeYes, I understand that. But I still don't see how this issue is different from having "normal" overloaded global functions.Read the manual. Overloads are only possible within one scope. That means, when defined on the module level, overloads only work correctly within that module. If defined within a class, only within a class. And so on.
Jan 07 2004
"Hauke Duden" <H.NS.Duden gmx.net> wrote in message news:bti1ij$1sv$1 digitaldaemon.com...Ilya Minkov wrote:Agreed.Hauke Duden wrote:I wasn't aware of that - thanks for explaining. So the problem IS the same with global functions, and it is not solved there either, just circumvented. That doesn't make it any better, though :(.Yes, I understand that. But I still don't see how this issue is different from having "normal" overloaded global functions.Read the manual. Overloads are only possible within one scope. That means, when defined on the module level, overloads only work correctly within that module. If defined within a class, only within a class. And so on.
Jan 07 2004
"Ilya Minkov" <minkov cs.tum.edu> wrote in message news:bthrlf$2p6n$2 digitaldaemon.com...Hauke Duden wrote:Even if you import foo and bar?? I thought that was the whole point of import - to bring symbols into the current scope. Is anyone really suggesting that this mechanism should work *without* requiring import statements?Yes, I understand that. But I still don't see how this issue is different from having "normal" overloaded global functions.Read the manual. Overloads are only possible within one scope. That means, when defined on the module level, overloads only work correctly within that module. If defined within a class, only within a class. And so on.If I have two modules, foo and bar, each with a different version of function f, and I call f in another module, isn't that the exact same problem, as if f was an opAdd overload and the call an expression of the kind (a+b)?The problem is, it doesn't work.Again, that is what "import" is for.I really don't see how global operators are different from global functions!They are not. That's the reason cross-module overloads don't work. They don't work for normal functions. And for operators, it is simply bloody forbidden to use module scope, because it would not make the problem better, just worse!
Jan 08 2004
Ben Hinkle wrote:Even if you import foo and bar?? I thought that was the whole point of import - to bring symbols into the current scope. Is anyone really suggesting that this mechanism should work *without* requiring import statements?Import doesn't bring a module into your scope. It only adds it to module search path, used when searching for function calls. All of the overloads of one function must be completed within 1 module. We had been already arguing with Walter about that. And that this approach is, well, at least somewhat "surprising". If you make overloads of one function in one module, and some in another, and import both in yours, only one set of overloads should work. The other should be acessible through the module name point notation only.They are not. That's the reason cross-module overloads don't work. They don't work for normal functions. And for operators, it is simply bloody forbidden to use module scope, because it would not make the problem better, just worse!Again, that is what "import" is for.Again, you don't seem to listen, nor to read! -eye.
Jan 09 2004
"Ilya Minkov" <minkov cs.tum.edu> wrote in message news:btmmt0$12nl$1 digitaldaemon.com...Ben Hinkle wrote:Importing a modules adds all the toplevel declarations to the current scope *but* what I had not considered was that functions are not exempt from the rule about ambiguous symbols (which if this is what you mean then yes, I think that is a problem with the language since that is the whole point of function overloading). Using aliases after importing does work with the current language spec, though I was assuming it wasn't needed for functions. For example: file1.d: import file2; alias file2.func func; import file3; alias file3.func func; int main(char[][] args) { func(1); func("hello"); return 0; } file2.d void func(char[] x) { printf("char[] func\n"); } file3.d: void func(int x) { printf("int func\n"); } ugh-ly.Even if you import foo and bar?? I thought that was the whole point of import - to bring symbols into the current scope. Is anyone really suggesting that this mechanism should work *without* requiring import statements?Import doesn't bring a module into your scope. It only adds it to module search path, used when searching for function calls. All of the overloads of one function must be completed within 1 module. We had been already arguing with Walter about that. And that this approach is, well, at least somewhat "surprising". If you make overloads of one function in one module, and some in another, and import both in yours, only one set of overloads should work. The other should be acessible through the module name point notation only.ouch!They are not. That's the reason cross-module overloads don't work. They don't work for normal functions. And for operators, it is simply bloody forbidden to use module scope, because it would not make the problem better, just worse!Again, that is what "import" is for.Again, you don't seem to listen, nor to read!-eye.
Jan 09 2004
Ilya Minkov wrote:Ben Hinkle wrote:> Import doesn't bring a module into your scope. It only adds it to modulesearch path, used when searching for function calls. All of the overloads of one function must be completed within 1 module.thats good to know, thanks
Jan 09 2004
What, do you have to use ..? with (myalgebra.opAdd) { myalgebra.vecter a,b,c; a = b + c; } that's the thing with C++, is that all operators are visible all the time, once they're imported, and the only restriction is that the types have to match one of the overloads or be convertible. You could go and choose one specifically, but you could obtain the same effect by casting the inputs. Most of the time you want all visible scopes searched for operators, not only an "outward" search that stops at the first match. I find this restrictive on enums as well. I should have to specify a particular enum type name only if the enum type actually has a name, or only if there is some conflict and I have to choose. Please get the visibility rules right. Sean "Ilya Minkov" <minkov cs.tum.edu> wrote in message news:btmmt0$12nl$1 digitaldaemon.com...Ben Hinkle wrote:Even if you import foo and bar?? I thought that was the whole point of import - to bring symbols into the current scope. Is anyone really suggesting that this mechanism should work *without* requiring import statements?Import doesn't bring a module into your scope. It only adds it to module search path, used when searching for function calls. All of the overloads of one function must be completed within 1 module. We had been already arguing with Walter about that. And that this approach is, well, at least somewhat "surprising". If you make overloads of one function in one module, and some in another, and import both in yours, only one set of overloads should work. The other should be acessible through the module name point notation only.They are not. That's the reason cross-module overloads don't work. They don't work for normal functions. And for operators, it is simply bloody forbidden to use module scope, because it would not make the problem better, just worse!Again, that is what "import" is for.Again, you don't seem to listen, nor to read! -eye.
Jan 11 2004
"Ilya Minkov" <minkov cs.tum.edu> wrote in message news:bthrlf$2p6n$2 digitaldaemon.com...Hauke Duden wrote: Read the manual. Overloads are only possible within one scope. That means, when defined on the module level, overloads only work correctly within that module. If defined within a class, only within a class. And so on.You can make it work by using an alias to overload a function in one module's scope with a function in another module's scope: import bar; void foo(int); alias bar.foo(uint) foo; Of course, this has to be done deliberately rather than implicitly. There isn't a global scope in D, which I think is a good thing when managing very large programs.If I have two modules, foo and bar, each with a different version of function f, and I call f in another module, isn't that the exact same problem, as if f was an opAdd overload and the call an expression of the kind (a+b)?The problem is, it doesn't work.
May 13 2004
if we disallow member-operators (wich i don't see useful anyways), then each operator has an unique name, and can only exist one time. i think you should simply drop any modularisation for operators. they are syntactic glue, nothing more. else we just use a using statement to import the operators of some module. shouldn't be that hard.. and else, just ADL around.. if needed. the feature is essencial for oo design, as well as for generic design. nobody wants to write output(output(output(output(buffer,"Hello, my name is "),name),", and i come from "),country); i see a one-one mapping from function to operator. i see why you don't. but i think the same rules can simply be applied.. and if you need the operators of some math library, wich you can only access by math.someFunction, how about this? with(math) { a += b; } (as a proposed using statement..) and else.. a math.+= b; doesn't look _THAT_ bad :D so you would not require the ADL thingy at all.. or too ugly? In article <bte4a7$1je$1 digitaldaemon.com>, Walter says..."davepermen" <davepermen_member pathlink.com> wrote in message news:bte3p3$pp$1 digitaldaemon.com...i don't see how the operator overloading can result in more work to "lookup"..if the parser finds some code snipped like this: a + b then it simply translates it to opAdd(a,b) directly. and then its just a normal function call.. and done. the normal operator rules for encoding/parsing code-lines with operators still apply,andthe endresult is pleasing, and with no issues in parsing at all..The problem is if opAdd() is defined in another module that is not the current module. It will not be found with the normal scoped lookup. With ordinary functions, one just does: foo.opAdd(a,b) if the function is in module foo. That doesn't work out so good for operator overloads. What ADL does is look at the types of the arguments to the function, and uses those types to add more scopes to look for the opAdd in.
Jan 06 2004
if we disallow member-operators (wich i don't see useful anyways), theneachoperator has an unique name, and can only exist one time. i think youshouldsimply drop any modularisation for operators. they are syntactic glue,nothingmore.<snip> How so? If you define a String class, and I define a Date class, are you saying that D should not support the ability for us to integrate them into, say, a generic serialisation library? Or to concatenate your string and my string? Or to work with your Transformation function and my Matrix? Is D to be extensible by users, like C and C++, or only by DM, like Java?
Jan 06 2004
Matthew wrote:How so? If you define a String class, and I define a Date class, are you saying that D should not support the ability for us to integrate them into, say, a generic serialisation library? Or to concatenate your string and my string? Or to work with your Transformation function and my Matrix?I think a workaround with RTTI (safe cast) and interfaces can be found for the rare cases where it matters. For example for strings and for the IO streams library. This would also mean that we could have a great advantage, if structs would be able to implement interfaces and could be casted to classes (of the same name) using automatic boxing. You would be advised to look at the library design of such a smart, pure object-oriented, multiple-inheritance, interface-based, add more buzwords here, language as Sather. ;) Bottom line: ADS is not necessary. Non-member operators are not necessary. Some thinking is. -eye
Jan 06 2004
Bottom line: ADS is not necessary. Non-member operators are not necessary. Some thinking is.show me how.
Jan 06 2004
unique per type-pair thats what i ment. In article <bteaec$akr$3 digitaldaemon.com>, Matthew says...if we disallow member-operators (wich i don't see useful anyways), theneachoperator has an unique name, and can only exist one time. i think youshouldsimply drop any modularisation for operators. they are syntactic glue,nothingmore.<snip> How so? If you define a String class, and I define a Date class, are you saying that D should not support the ability for us to integrate them into, say, a generic serialisation library? Or to concatenate your string and my string? Or to work with your Transformation function and my Matrix? Is D to be extensible by users, like C and C++, or only by DM, like Java?
Jan 06 2004
Hear, hear! "davepermen" <davepermen_member pathlink.com> wrote in message news:btbq8o$2g9e$1 digitaldaemon.com...Walter this is essencial. not only for streaming libraries, but simply ingeneral.. anTc = Ta + Tb doesn't have ANY preference for any of those 3 types. itshould beTc opAdd(Ta a,Tb b) {} and not Tc Ta.opAdd(Tb b) {} or Tc Tb.opAddR(Ta a) {} or even worse, both! just get rid of it. binary operators don't belong to a member. they neverdid.and if you release that essencial missconcept, and replace it by the onlyrealone, we gain immediate access to a very fast implementable typesave, and extendable streaming library.. Tc operator add(Ta a,Tb b) { } would be my prefered way.
Jan 05 2004
It seems that operator overloads should belong at the module level since they are really just syntactic sugar for functions. The module would define and "export" the types then the module would define and "export" the allowed operators on those types. Since D has strong typedefs this shouldn't be too much of a problem. This should also allow operator overload mixtures of standard primitive types with module defined types. If you need to add more types and operators then the module needs changing, since you have changed the specification of that module. Of course, the module should be cohesive but that is up to the developer. See Ada for a language that has been doing this for a long time: http://www.grammatech.com/rm95html-1.0/rm9x-06-06.html http://www.adaic.com/docs/95style/html/sec_5/5-7-4.html In article <btbq8o$2g9e$1 digitaldaemon.com>, davepermen says...Walter this is essencial. not only for streaming libraries, but simply in general.. an Tc = Ta + Tb doesn't have ANY preference for any of those 3 types. it should be Tc opAdd(Ta a,Tb b) {} and not Tc Ta.opAdd(Tb b) {} or Tc Tb.opAddR(Ta a) {} or even worse, both! just get rid of it. binary operators don't belong to a member. they never did. and if you release that essencial missconcept, and replace it by the only real one, we gain immediate access to a very fast implementable typesave, and extendable streaming library.. Tc operator add(Ta a,Tb b) { } would be my prefered way.
Jan 07 2004