Re: [Alephmodular-devel] Random rumblings on files and errors
Status: Pre-Alpha
Brought to you by:
brefin
From: Woody Z. I. <woo...@sb...> - 2003-01-30 18:54:13
|
Ok I think we are in agreement on most things. But I can't resist picking at the reference thing a little more. On Thursday, January 30, 2003, at 10:56 AM, Br'fin wrote: > One of the books I was reading suggested always using return parameters > for important information. It's harder to ignore a return parameter > than a return value. > > So while > create_foo(); // Returns NULL or new object > > is legal and the compiler won't stop you, it is definitely wrong. Interesting point, yes. This should be weighed against the potential convenience benefits of a return value occupying a temporary rather than an explicit variable. (I suspect you'll still come out in favor of the explicit code... which is interesting given the stance we'll see later.) I guess maybe the key word is "important". I can picture many cases where a function would be able to provide information that the caller *might* care about, but might not. > I don't really see it as a least common denominator approach either. Hmm it seemed to me an attempt to take away all variation in the return type of functions, which sure feels like it needs to sit at the LCD. >> And on using foo*& outFoo as a parameter rather than foo**: isn't the >> latter more familiar and thus more comfortable to most programmers? >> Is there any advantage to the former other than the programmer not >> having to type an extra & when the variable is passed? I'd even argue >> that having to put that & there is somewhat of an advantage - at least >> every time you do so you are explicitly reminded that the value of >> what you're passing could be changed. > > On one hand I'm kind of mixed and ambiguous on this. On the other, the > C++ FAQ tends to recommend the use of references, and actually mentions > that converting a derived** to a base** can be flagged as a compiler > warning or error. > > [21.2] Converting Derived* -> Base* works OK; why doesn't Derived** -> > Base** work? > http://geneura.ugr.es/~jmerelo/c++-faq/proper-inheritance.html#faq-21.2 Right, this is good behavior - I felt this to be the case even before I read the FAQ entry, though my thinking was in the context of _our_ discussion: If I rewrite the function Base* foo(); to void foo(Base**); then I should not be able to call Derived* bar; foo(&bar); any more than I should be able to call Derived* bar; bar = foo(); There's no problem 'in reverse' inside the implementation of foo(), either, since it dereferences the argument before working with it: void foo(Base** outBar) { if(outBar != NULL) { Derived* baz = (get a Derived* somehow); *outBar = baz; } } and in this case I'm assigning a Derived* to a Base* which is allowed. Oh AHA the problem comes when I have (ok, clear out all identifiers from above ;) ) Derived* foo(); void foo(Derived** outBar); then Base* bar = foo(); is fine, but Base* bar; foo(&bar); is a problem because the argument is a Base** and needs to be converted to match the formal parameter Derived**. Hmm but that's good, because if 'bar' is really pointing at an object of type OtherDerived, we don't want foo(Derived**) interpreting it as a Derived. And we don't know what foo(Derived**) is going to do inside there. So all this boils down to: If 'foo' gives us a derived*, we must put it in a derived* variable. Whether it's an explicit variable in our code or an implicit temporary is irrelevant. In other words, all of these work: Base* bar; Derived* foo(); bar = foo(); Derived* temp; void foo(Derived** outBar); foo(&temp); // can't do foo(&bar); bar = temp; void foo(Derived*& outBar); foo(temp); // can't do foo(bar); bar = temp; Base* foo(); bar = foo(); void foo(Base** outBar); foo(&bar); void foo(Base*& outBar); foo(bar); ... so the long and short of it seems to be, both the reference approach and the pointer approach have the same properties. Only the return-value approach offers concise syntax in each case, precisely because it's the only approach that can guarantee that foo() is not reading from the pointer we're trying to initialize. Food for thought. Poke the holes if you see them. > [8.6] When should I use references, and when should I use pointers? > http://www.parashift.com/c++-faq-lite/references.html#faq-8.6 Funny, I didn't realize I'm an "Old line C programmer" :)... this FAQ entry states (among other things): > Note: Old line C programmers sometimes don't like references since they > provide reference semantics that isn't explicit in the caller's code. > After some C++ experience, however, one quickly realizes this is a form > of information hiding, which is an asset rather than a liability. ... but it gives no example of this being an asset, and I can't think of any in our current context at the moment. (To be fair, I haven't worked with references much.) And given the other choices to make things explicit in the code, it seems strange to choose something that hides the code's behavior. Any thoughts? >> And indeed, there's an advantage to the latter: code can be written to >> ignore NULL passed in there, so if the programmer doesn't need some >> returned values, he can just pass in NULL rather than make a dummy >> variable that clutters things up. > > If we find a situation where we're ditching the return value like that, > then I would think our API or the function trying to call it like that > needs tweaking. I agree that an API has a responsibility to guide the programmer toward correct usage. OTOH I don't think it should rule with an iron fist. It's just like how a good Mac UI guides the user toward correct usage... but if a developer gets carried away and his UI starts forcing the user to do things a certain way because the UI "knows better" than the user, things go sour. The user can't do what the user wants to do because the UI gets in the way. I'm sure we've all used a program like this at some point. Anyway let's try not to be _overly_ restrictive or 'draconian' in the API design - and let's not bog the programmer down with so many minutiae that calling a function takes more code than implementing the function. I have to admit, I don't have a whole lot of experience with exceptions in general - and least of all exceptions in C++ - but they seem like a very valuable way of both {making sure errors get noticed and appropriately handled} _and_ not bogging the programmer down with a dozen lines of code supporting every function call. Investigation is probably in order. > I'm going to accept that even once I'm done with things like the file > abstraction, that something will turn up down the road where I missed X > or I missed Y. We shouldn't try to work around this kind of situation, > we should fix it. I have no idea what semantic value I should extract from this paragraph. Are you encouraging discussion in general? Are you giving rationale for putting off coding the File stuff to talk about calling conventions? Are you trying to state 'a good API guides the programmer toward correct usage'? I haven't the vaguest. No offense intended - I just value your comments and want to make sure I actually understand all of them. :) Woody |