Re: [Alephmodular-devel] Random rumblings on files and errors
Status: Pre-Alpha
Brought to you by:
brefin
From: Br'fin <br...@ma...> - 2003-01-30 21:25:33
|
Something that seems to be going on throughout our discussions is a three-way combat of priorities: speed, readability, and correctness. I'm guilty of championing one over another at different times. I agree. For action flags, I had been championing speed, in part because it seemed we'd be adding a lot of overhead to a presumably tight area. Readability is how nice the code is to read. How understandable is it? Correctness is perhaps the wrong term. But it's an emphasis on using the compiler to keep us from doing stupid things. I don't see it at great odds with readability. Either way, the developer should know the arguments to the functions. And passed pointers and references had the potential for being unclear to begin with. It's not like I made a great distinction between constant and malleable strings ever before, both generally get passed as some kind of char*... By turning things around so that important data must be an argument that I can write to instead of being a return variable, it's much harder to just ignore the return info without explicitly choosing to do so. Similarly, I think the way I was using references fits the bill as well. Which bill? The Correctness one. The compiler knows more about how we're using the value, and we're typing in one less explicit get-pointer/dereference pointer pair. Worrying about the speed of an explicit variable versus a temporary one seems to be misplaced concern. If our speed really is that sucky I'm sure there are going to be better areas to target. On Thursday, January 30, 2003, at 01:53 PM, Woody Zenfell, III wrote: > 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'm curious about what kinds of situations those might be? >> 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. > Ahhh, gotcha. To be truthful, I'm most inclined to do it in cases where the return value is extra important. Like some bit of memory that will need to be explicitly freed. I don't see so much of a need when you're getting, say, the height and width of a rectangle class. >>> 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: [snip] > 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. I tried thinking of a case, but it duplicated one of your existing ones. :) At any rate very very messy with the pointers. It seems. > 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? One thought is, think who wrote the FAQ. A bunch of C++ promoters who send a lot of responses through comp.lang.c++. There's not a lot I can say that probably isn't already covered by other answers on that particular page. But certainly there's a big difference in the following int p; int *ptr = p; int &ref = p; // This assignment might be off, but ref should now *BE* p; ptr++; // increments the pointer to whatever's after p ref++; // increments p Admittedly by doing bool get_named_child(const char *name, IFileSystemDesc* &out_desc) const = 0; I have the following guarantees that the compiler can enforce: IFileSystemDesc* is a real pointer I can reset. I don't have to check whether or not the user is trying to pull a fast one and sending an unintended NULL. The compiler has more information to enforce what kind of pointer is being passed to me. I no longer need to double-check all the references/dereferences. Grr, my arguments could be better. :) >>> 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 am worried about doing that all of that, being too draconian or overengineering things. However I don't think I am being overly restrictive in this case (The file abstraction) > 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 more familiar with Java exceptions than C++ ones. Then again, my C++ training, unlike my Java training, tends to lean towards 'yes we have exceptions' instead of explaining how exceptions work and having a nice library and framework for handling them the way Java does. Definitely true on the investigation. >> 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. :) I was trying to state that the APIs should be mostly stable, not carved in stone. I would like to say they're perfect, but I don't have that much of an ego. However, if an issue comes up down the line where the APIs don't perform a needed function, then fixing the APIs is valid instead of saying 'oops, oh well, can't change them now, we better work around them.' I've begun mucking the file code, honest. That's why some of these other issues are coming up. They hadn't made it into my thinking during the drafting process. So as I begin to organize code, other details are falling into place or appearing and need to be put someplace :) -Jeremy Parsons |