Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.

Due to spam on this forum, all posts now need moderator approval.

 Entire forum ➜ SMAUG ➜ SMAUG coding ➜ Converting from GCC to G++

Converting from GCC to G++

It is now over 60 days since the last post. This thread is closed.     Refresh page


Pages: 1  2 

Posted by Twisol   USA  (2,257 posts)  Bio
Date Reply #15 on Fri 19 Mar 2010 05:55 PM (UTC)
Message
David Haley said:
The 'out' parameter is another way to deal with this, but it (as usual) depends on what exactly is happening here. Sometimes it's more convenient to have the input operated on directly for whatever reason. For instance, it might be a more complicated data structure than just a char*. Also, it means more stuff to keep track of on the caller's end when they want to get the modifications; now they have to make the call and prepare the target buffer for the modifications. Like I said, it really depends on what the common case is.

It's also completely possible to call it with the same variable as both the second and third argument, i.e. interpret(ch, command, command)

David Haley said:
All I was trying to say here is that there are reasonable cases besides "always care" and "never care", and so it can make sense to have this form of overloading. (Sometimes, the implementations might even be different, and the overloaded version wouldn't wrap it with a duplication but do some other computation that doesn't involve the side effect.) So the answer isn't a clear-cut "make it either const or non const but not both". Even in the case of the nullable out parameter, we see that this is happening (although out parameters, especially for some types in C/C++, have a whole new slew of issues of their own...).

Of course. Again, I'm just trying to keep the original issue in mind here.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #16 on Fri 19 Mar 2010 06:04 PM (UTC)
Message
Twisol said:
It's also completely possible to call it with the same variable as both the second and third argument, i.e. interpret(ch, command, command)

Most of the time, that is in fact very dangerous. It's like working on input as you're pulling it out from under yourself. Most functions with in/out parameters require that they point to different things, or else much confusion results.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Twisol   USA  (2,257 posts)  Bio
Date Reply #17 on Fri 19 Mar 2010 06:10 PM (UTC)
Message
David Haley said:

Twisol said:
It's also completely possible to call it with the same variable as both the second and third argument, i.e. interpret(ch, command, command)

Most of the time, that is in fact very dangerous. It's like working on input as you're pulling it out from under yourself. Most functions with in/out parameters require that they point to different things, or else much confusion results.

Fair point. My point was just that it would be basically exactly the same thing as an interpret() with only a char*.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #18 on Fri 19 Mar 2010 06:51 PM (UTC)
Message
I guess I don't see how it's the same thing, because you're likely to have to duplicate it so that the program won't be overwriting the buffer it's reading from.

If your function is foo(const char* in, char* out), you most likely wouldn't want to pass the same char* to both, unless the function explicitly says that it is safe to do so. (This might be the case for a function that converts a string's case, for instance, so it only writes over input positions that it has already read.)

A color converter, for example, shows quite clearly how disastrous it can be to pass in a single string, because you'll be expanding color codes into the input you haven't seen yet.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Twisol   USA  (2,257 posts)  Bio
Date Reply #19 on Fri 19 Mar 2010 07:08 PM (UTC)
Message
Put it this way... A "widening" type conversion, like conversion from a char to a long, is valid and safe. The reverse is not true, as you may be losing data in the higher bits. This is the same thing going on here; I'm widening from the original foo(char*) to foo(const char*, char*), whereas you're looking at it in the reverse direction. Assuming that both versions of the function still do the same thing, it should be legal to call foo()/2 with the same argument twice, which effectively degenerates to the original foo()/1.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #20 on Fri 19 Mar 2010 07:16 PM (UTC)
Message
I don't understand the notation you're using, sorry. :)

I think what you're saying is that if the single-parameter version was safe, then it must not be overwriting its input anyhow. Well, that's not necessarily the case, either: it might be duplicating the input stream internally as a convenience to the caller, so that they pass in some input, and it comes back modified, without needing to worry about creating a buffer themselves.

Basically, my point here again is pretty simple: if a function has in and out parameters, you should be wary of using the same memory for both, unless the documentation explicitly allows it.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Twisol   USA  (2,257 posts)  Bio
Date Reply #21 on Fri 19 Mar 2010 07:26 PM (UTC)
Message
David Haley said:
I don't understand the notation you're using, sorry. :)

Arity: I blatantly stole it from Erlang. foo()/2 means the version of foo() with 2 arguments. (Strictly speaking, I have the () in the wrong place, but I'm just using it descriptively)

David Haley said:
I think what you're saying is that if the single-parameter version was safe, then it must not be overwriting its input anyhow. Well, that's not necessarily the case, either: it might be duplicating the input stream internally as a convenience to the caller, so that they pass in some input, and it comes back modified, without needing to worry about creating a buffer themselves.

I was assuming that foo()/1 does modify its input string, otherwise I'd make it a const char*. If foo()/1 is safe, and it modifies its input string, then foo()/2 should be safe, assuming you replace the relevant variable references with 'out' instead. But you shouldn't have to change the logic, at all.

David Haley said:
Basically, my point here again is pretty simple: if a function has in and out parameters, you should be wary of using the same memory for both, unless the documentation explicitly allows it.

In general I agree. I still believe that a "widening" conversion gives you an implicit guarantee, at least until you actually change the logic in foo()/2.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #22 on Fri 19 Mar 2010 07:33 PM (UTC)
Message
Twisol said:
I was assuming that foo()/1 does modify its input string, otherwise I'd make it a const char*. If foo()/1 is safe, and it modifies its input string, then foo()/2 should be safe, assuming you replace the relevant variable references with 'out' instead. But you shouldn't have to change the logic, at all.

No, I disagree with this.

Assume that foo(X*) is modifying its input string by first copying it, and then using the copy as input, and writing output back to the parameter. Also assume that foo(const X* in, X* out) assumes (rather reasonably) that the input and output buffers are different, and therefore does not need to copy its input. Therefore, it will be writing into the output even though it is squashing its input.

In other words, in the first case it might use the parameter memory as both input and output, and does this safely because it copies the input stream before operating on the parameter's memory.

So: it's not safe to assume that just because things work with the single-parameter version, they'll necessarily work the same way with the two-parameter version.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Twisol   USA  (2,257 posts)  Bio
Date Reply #23 on Fri 19 Mar 2010 07:37 PM (UTC)

Amended on Fri 19 Mar 2010 07:42 PM (UTC) by Twisol

Message
David Haley said:
Also assume that foo(const X* in, X* out) assumes (rather reasonably) that the input and output buffers are different, and therefore does not need to copy its input.

There, right there, you are changing the logic of the original function, and breaking the guarantee I postulated.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #24 on Fri 19 Mar 2010 07:47 PM (UTC)
Message
What guarantee exactly am I breaking?

You can modify your input, without it being safe to have a version that reads and writes the same memory location.

You might be saying that you want the two-argument version to do the exact same thing as the one-argument version internally, just write to the second param. It would therefore still be duplicating the input parameter internally, because it's doing the exact same thing. If this is what you are saying, then ok, but this is kind of silly (it defeats the whole point of having a const parameter!) and regardless is not an assumption that can be made generally.

You can't assume, when looking at things in general, that just because it's safe to call the one-parameter version, it's also safe to call the two-parameter version with the same buffer unless special precautions have been taken (and the API documentation would presumably make this clear).

Obviously we can contrive any number of cases in which this would be safe, but I'm trying to make statements about general programming practices, not an extremely specific case. What got us into this tangent were comments made about constness and overloading in general, not on some specific case we make up. See in particular my first comment in this thread.

And even if you want to get into specifics, namely the SMAUG 'interpret' function, my previous comments argue that you want to make life as convenient as possible for the caller, and that your suggestions reduce convenience for the caller because they force the creation of a writable buffer, which is annoying when all you have is a const buffer: you need to do the duplication yourself. By contrast, if you have an overloaded version that hides that from you, you don't need to worry about it.

I guess I'm not sure what you're trying to argue. :-)

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Twisol   USA  (2,257 posts)  Bio
Date Reply #25 on Fri 19 Mar 2010 07:57 PM (UTC)
Message
David Haley said:
I guess I'm not sure what you're trying to argue. :-)

Me either, anymore. =/ I guess I just enjoy a good discussion.

I was originally trying to show how you probably don't need an overloaded function at all. and assuming that either the modifications were interesting or they weren't. Since the function returns void, the parameter is the only way to get any data back out of the function. So if it's interesting, use char*; if it's not interesting, use const char* and strdup(). In the first case, yes, I'm assuming the general case is that you want the interesting modifications. If you don't, well, I guess that's what the original post's overload was for. But that's far too implicit for my tastes, and I don't think overloading is a good fit for that.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
Top

Posted by Nick Gammon   Australia  (23,140 posts)  Bio   Forum Administrator
Date Reply #26 on Sat 20 Mar 2010 01:49 AM (UTC)
Message
Twisol said:

Since the function returns void, the parameter is the only way to get any data back out of the function.


No, the function has side-effects. So for example interpret ("who") shows a who list. The function interpret does not return data, per se.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Twisol   USA  (2,257 posts)  Bio
Date Reply #27 on Sat 20 Mar 2010 01:50 AM (UTC)

Amended on Sat 20 Mar 2010 01:51 AM (UTC) by Twisol

Message
Nick Gammon said:

Twisol said:

Since the function returns void, the parameter is the only way to get any data back out of the function.


No, the function has side-effects. So for example interpret ("who") shows a who list. The function interpret does not return data, per se.


I meant that there's nothing that the function does that the calling code sees straight away. Of course it has side effects, that's the point of the function. ;) But the side-effected data is sent to the user over a socket, not back out of the interpret function, correct?

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
Top

Posted by Nick Gammon   Australia  (23,140 posts)  Bio   Forum Administrator
Date Reply #28 on Sat 20 Mar 2010 02:01 AM (UTC)
Message
Twisol said:

I meant that there's nothing that the function does that the calling code sees straight away. Of course it has side effects, that's the point of the function. ;) But the side-effected data is sent to the user over a socket, not back out of the interpret function, correct?


Yes, in this case.

However some functions (like math.floor for example) only work on the provided data, and return a result. Thus (in C at least) if they need to modify lots of things, you need to provide them with non-const arguments which are not simply call-by-value.

I am making the point that for some functions you may not *need* to get data back out of them, if they operate only by causing side-effects.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Twisol   USA  (2,257 posts)  Bio
Date Reply #29 on Sat 20 Mar 2010 02:03 AM (UTC)
Message
Nick Gammon said:
I am making the point that for some functions you may not *need* to get data back out of them, if they operate only by causing side-effects.


That, by definition, is getting data back out of them, at least in my book. It's what I meant earlier about C#'s "out" parameter modifier.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


71,121 views.

This is page 2, subject is 2 pages long:  [Previous page]  1  2 

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.