alephmodular-devel Mailing List for AlephModular (Page 13)
Status: Pre-Alpha
Brought to you by:
brefin
You can subscribe to this list here.
2002 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(61) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2003 |
Jan
(193) |
Feb
(61) |
Mar
(55) |
Apr
(5) |
May
(3) |
Jun
(1) |
Jul
(3) |
Aug
(14) |
Sep
(19) |
Oct
(48) |
Nov
(6) |
Dec
(25) |
2004 |
Jan
(1) |
Feb
|
Mar
(2) |
Apr
(6) |
May
(1) |
Jun
(1) |
Jul
(1) |
Aug
|
Sep
|
Oct
(1) |
Nov
|
Dec
|
2005 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(17) |
Jun
(20) |
Jul
(13) |
Aug
|
Sep
|
Oct
(1) |
Nov
|
Dec
|
2008 |
Jan
|
Feb
|
Mar
|
Apr
(2) |
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: Br'fin <br...@ma...> - 2003-02-05 02:42:16
|
I've been poking at the files and trying to figure out how to integrate new code. So one of the things is that Apple has these functions, MoreFileX.h/.c. Which has useful code for handling files on Macintosh. And I'm thinking along these lines: source_code_dir/ Support/ AStream.h Astream.cpp CError.h Macintosh/ MoreFilesX.h MoreFilesX.c Hsm, and if a situation warranted a subdir, then I might as well also put CFiles_Macintosh.h/.cpp into such a subdirectory as well.. -Jeremy Parsons |
From: Br'fin <br...@ma...> - 2003-02-04 01:53:47
|
Ok, after some more working and thinking, this particular code snippet now comes out like: if(AlephModular.bad()) { CError err = AlephModular.get_error(); dprintf("Er: %ld type: %d", err.get_code(), err.get_type()); AlephModular.clear_error(); } Two things to note here. is_good() was replaced with good() and bad() (Though I resisted the temptation to go the whole way that iostream does which overrides things so if(cin) is the same as if(cin.good()) and if(!cin) is the same as if(cin.bad())) :) And the other is I renamed GameErrorState to be AlephModular. Why? Well, the specific class that AlephModular is a variable of is called GameState. And it seems that such a class would be a reasonable 'owner' of all the dynamic info in the game. For instance, AlephModular.save_game(IFileDesc) would dump out everything the game needs to reload itself. I admit that this sort of dynamic ownership of game state is still off in the future, but it does seem to be food for though. I admit that game_errors.h isn't the best place for this class in the end, but seeing as its subsuming the game_errors.h functionality it seemed like the best place to drop it initially. -Jeremy Parsons On Sunday, February 2, 2003, at 01:45 PM, Br'fin wrote: > I decided to just focus on implementing errors for a bit. Implement > it, check it in, then get back to files. > > So I've been working on game_errors.h and replacing the functionality > in there with an implementation of IFailable. This isn't the best > example since it's an effect that looms over the whole game code, but > replacing usage of set_game_error and such with an IFailable interface > seemed a good way to neaten the IFailable stuff up. As a result I did > find some places where returning a constant reference is better than > dealing with passing the reference. > > (Note in the sample below how the error is retrieved from the class) > > Old method: > if(error_pending()) > { > int16 type; > > err= get_game_error(&type); > dprintf("Er: %d type: %d", err, type); > set_game_error(systemError, noErr); > } > > New method: > if(!GameErrorState.is_good()) > { > CError err = GameErrorState.get_error(); > dprintf("Er: %ld type: %d", err.get_code(), err.get_type()); > GameErrorState.clear_error(); > GameErrorState.set_error(CError(CError::systemError, > CError::errNone)); > } > > As a result of this, I must say that I am waffling over is_good and > error_pending as that is the only part I really feel unhappy with. > > Oh, and I suppose GameErrorState is lame too. But I couldn't tell you > when we might have good and proper top of the tree state objects for > the game. > > -Jeremy Parsons |
From: Br'fin <br...@ma...> - 2003-02-02 18:45:17
|
I decided to just focus on implementing errors for a bit. Implement it, check it in, then get back to files. So I've been working on game_errors.h and replacing the functionality in there with an implementation of IFailable. This isn't the best example since it's an effect that looms over the whole game code, but replacing usage of set_game_error and such with an IFailable interface seemed a good way to neaten the IFailable stuff up. As a result I did find some places where returning a constant reference is better than dealing with passing the reference. (Note in the sample below how the error is retrieved from the class) Old method: if(error_pending()) { int16 type; err= get_game_error(&type); dprintf("Er: %d type: %d", err, type); set_game_error(systemError, noErr); } New method: if(!GameErrorState.is_good()) { CError err = GameErrorState.get_error(); dprintf("Er: %ld type: %d", err.get_code(), err.get_type()); GameErrorState.clear_error(); GameErrorState.set_error(CError(CError::systemError, CError::errNone)); } As a result of this, I must say that I am waffling over is_good and error_pending as that is the only part I really feel unhappy with. Oh, and I suppose GameErrorState is lame too. But I couldn't tell you when we might have good and proper top of the tree state objects for the game. -Jeremy Parsons |
From: Br'fin <br...@ma...> - 2003-02-01 19:38:45
|
And still more thinking on errors, but more details too, I'm liking the approach below: /////////////////////////////////////////////////////////////////////// // // $Id: CErrror.h $ /////////////////////////////////////////////////////////////////////// // /* * CErrror.h * AlephModular * * CError defines an interface for classes that can error an go into a * bad state. For instance, opening a file fails so you need to report * this back to the calling function. * * The basic method this supports is the following: * A class has a method this_may_fail that returns a boolean (If the * method needs to return something meaningful like an opened file * reference, then this is done with out arguments passed by * reference) If the method is successful, then it returns true. * Otherwise it returns false and puts the class into a failed state. * You may get a class's current state to report why an operation * failed. * * Created by Br'fin on Thu Jan 30 2003. * */ #ifndef __CERROR_H #define __CERROR_H #include "cseries.h" #include <string> class CError; /* * IFailable * Interface * * This is an interface to be supported by all classes that may fail. * For instance, opening a file may fail. * */ class IFailable { protected: /* * set_error * arguments: * an explanatory member of CError that explains the current error */ virtual void set_error(const CError &) = 0; public: /* * is_good * returns: * true if and only if there is no error state on the object */ virtual bool is_good() const = 0; /* * get_error * arguments: * returns the objects's current error state. */ virtual void get_error(const CError &) const = 0; /* * clear_error * This attempts to clear the error state of the object. This * is not guarunteed to succeed. * returns: * true if the error can be cleared. False otherwise */ virtual bool clear_error() = 0; virtual ~IFailable() {} // Required virtual destructor }; /* * -Jeremy * We could go with all sort of subclasses and introspection and details. * However, that gets too convoluted for our needs. Like hitting a nail * with a pile driver. And doesn't sit right with me. * * CError describes an error that occured in one part of the system. * * If the error occured in some part of the operating system, then the * error's type should be systemError and the err_code can be an OS * error code. But should conform to an agreed upon external code. * For instance a file not found value. * * On the other hand, a gameError is tied to a state that AM must * report to the user. And in many cases, one system turning up * a systemError will have that error inspire a gameError to * display to the user. * * Why this disjoint? Well the gui should be familiar with what * gameErrors there are, and have access to appropriate resources * and localization information to display them to the user. * * So, what is a CError exactly? * It is a type of error with a given code. As explained above. And * allows a string of details to be provided. There is no gauruntee * that the details will be displayed. */ class CError { public: typedef enum { /* types */ systemError, gameError } type; typedef enum { /* Game Errors */ errNone= 0, errMapFileNotSet, errIndexOutOfRange, errTooManyOpenFiles, errUnknownWadVersion, errWadIndexOutOfRange, errServerDied, errUnsyncOnLevelChange, NUMBER_OF_GAME_ERRORS } game_errors; private: type err_type; int32 err_code; std::string details; public: CError(type _type, int32 _code, std::string _details = "") : err_type(_type), err_code(_code), details(_details) { #ifdef DEBUG if(err_type==gameError) assert(err_code>=0 && err_code<NUMBER_OF_GAME_ERRORS); #endif } CError(const CError& err) : err_type(err.err_type), err_code(err.err_code), details(err.details) {} CError& operator=(const CError err) { err_type = err.err_type; err_code = err.err_code; details = err.details; return *this; } type get_type() { return err_type; } int32 get_code() { return err_code; } void get_details(std::string& _string) { _string = details; } bool has_details() { return details.length() > 0; } }; #endif |
From: Br'fin <br...@ma...> - 2003-02-01 16:20:29
|
Mostly still thinking out-loud myself. Mostly with the errors I want to keep things simple and sweet. While the existing error handling isn't anything much to speak of in terms of threading or such, it is simple and sweet. There's no convoluted class hierarchy, there's no tons of arguments. And C++ exceptions almost seem like they come with more baggage than they solve. Grrf -Jeremy Parsons |
From: Dietrich E. <die...@zd...> - 2003-02-01 06:35:31
|
On Friday, January 31, 2003, at 07:23 , Br'fin wrote: > In general, I like the design I put forth for IFailable. It's intent > was not to cover an object handled by several threads at once. > The area I'm having problem is with with CError objects. As I work on > it, I'm going lets add this and lets add that. And consequently it > seems to be lacking a proper elegance to it. > > > Marathon handles errors like so: > > The game hits an error and calls set_game_error(type, code) > > Type may be a gameError or a systemError. If it's a gameError, than > code is limited to a particular subset of codes. If it's a systemError, > than the particular error is recorded in the code. > > Code higher up the hierarchy can check if the game has an > error_pending, it can read what kind of error is around with get_error > and can clear the error. > > > For instance, file finding code might report (systemError, > fileNotFound) Further up the code sees this and says: Error pending? > Ok, what is it? File not found? Ok, it's just the preferences, clear > the error and continue on to create the file. > > > For a contrasting viewpoint on errors, I looked at Java. That has the > hierarchy of 'Throwable' leading to 'Exception' (things which your > program should catch) and 'Error' (Things which your program shouldn't > catch, they're just too big). And then further breakdowns of errors. > > In our case this would work out to one SystemException and then > GameException with a hierarchy of specific exceptions beneath it. Maybe... then maybe the exact circumstances under which an exception is thrown should be better defined first. <thinking-out-loud> Runtime exceptions happen in I/O, system calls, and other juicy things like buffer overflows. Logic errors happen anywhere you shouldn't pass a nil pointer, or something is out of range, etc. </thinking-out-loud> It seems that runtime exceptions should be caught, interpreted, and presented to the user in a friendly manner such as "The shapes file is corrupted." or "The network connection vanished.", whereas logic errors should present themselves as bug reports - "Range error thrown at foo.cpp:273, caught at bar.cpp:450. Please report this to <bug...@bl...>." Logic errors could also be marked as irrecoverable, so after the bug report is presented the game quits. > Another thing about Java exceptions is that the interface is fairly > simple, You create a new exception and the string argument in the > creator is optional. > > > Hsm, was just thinking about exceptions and C++. Exceptions get to be a > nuisance if the current function itself is just a pass through for an > exception. (open_preferences would continue to throw a > fileNotFoundException that originated in open_file and also had to > propagate through wad_open.) Or do you just need to pussyfoot around > your declarations to make sure that if you're passing an exception that > you didn't declare any memory before the point where the exception can > occur? It's not really that weird, just C++ isn't garbage-collected like Java is. You can deallocate in a catch block or by using destructors. The auto_ptr's will free their pointees automagically. On Thursday, January 30, 2003, at 04:39 , Br'fin wrote: > My first instinct is 'what on earth do we have for objects that are > accessed in multiple threads *AND* which are failable'. > > As for existing error handling, I was looking at game_errors.h/.cpp and > some of how iostream type stuff does it. In the case of an iostream, > the stream itself does get into bad states (The object is affected) Well, yah. File handles have states, so there is no possible situation in which error is independent of state. But other objects aren't like that, and multiple error-handling systems would be ugly. > A quick perusal of set_game_error (Current error handling) reveals the > majority are around file handling (basic, wad, and preferences) and a > couple netgame spots. (Inability to sync with others or server died) > > In the case of Marathon, the game itself goes into a bad state until > falling far enough back in its main thread to correct it and/or report > to the user. This would be a heck of a lot cleaner without explicit error checking, as exceptions provide. I think I'd enjoy these concepts a whole lot more if I didn't absolutely hate C++. |
From: Woody Z. I. <woo...@sb...> - 2003-01-31 16:55:11
|
On Friday, January 31, 2003, at 09:23 AM, Br'fin wrote: > Hsm, was just thinking about exceptions and C++. Exceptions get to be a > nuisance if the current function itself is just a pass through for an > exception. (open_preferences would continue to throw a > fileNotFoundException that originated in open_file and also had to > propagate through wad_open.) Or do you just need to pussyfoot around > your declarations to make sure that if you're passing an exception that > you didn't declare any memory before the point where the exception can > occur? > IIRC C++ is not as picky as Java with respect to declaring 'throws' etc., which could be viewed as a mixed blessing I suppose. As for allocating memory, well your use of the phrase 'declaring memory' concerns me - if a function does int i; or char myStorage[40]; , the memory needed for those operations is taken from the stack, and will be released automatically when the stack is unwound during the exception propagation. OTOH allocating memory with new etc. does take some care, because the exception mechanism (understandably) won't try to follow through the remainder of a function it's unwinding to see if there are any delete's in there. So typically I think C++ programmers would use a stack-based wrapper around dynamically-allocated storage... that is, you have a class that's something like (I'm making this up, I'm sure there are better ways to do these things out there:) template class Autoreleaser<typename tType> { tType* mPointer; public: Autoreleaser(tType* inPointer) { mPointer = inPointer; } ~Autoreleaser() { if(mPointer != NULL) delete mPointer; } }; bool foo() { Blah* theBlah = new Blah; Autoreleaser<Blah> theBlahAutoreleaser(theBlah); // do I have to specify the type explicitly as a template argument? do_some_stuff_that_might_throw(); if(something) return false; do_more_stuff(); return true; } This way (since theBlahAutoreleaser's destructor will be called) theBlah will get deleted when foo() exits either by falling off the end, returning from somewhere, or through exception unwinding. You could always have a method "DontRelease()" or something in Autoreleaser that you could call at the end of your function if you wanted to keep the pointer in a successful operation, etc. I think the C++ Standard Library type 'auto_ptr' might include the above functionality among others. I haven't studied or used it yet. But if it's appropriate, I of course support using the standard mechanism rather than homegrown. My Logging mechanism in A1 is intended for such stack-based use: you use the utility macro logContext1("trying to gather player %s", thePlayerName);, say, and it pushes that context onto the Logger's stack. When the block that 'owns' the stack-allocated variable (hidden from you by the macro) exits, one way or another, that context description is popped off the stack - no explicit action is needed in the code. Log messages emitted with one of the standard Logging macros (logError2("unable to establish connection to %d:%d", theAddress.host, theAddress.port);) thus can be prepended with some or all of their enclosing contexts (but the context switches themselves don't have to be logged if no log messages are emitted within them). The same logging message could be entered from a variety of runtime contexts (e.g. having problems opening a file could happen during startup, or while opening a film, or while trying to load a replacement texture, etc.). The low-level routine logs its problem and depends on the higher-level routines having established a context ("loading replacement texture file '%s' for collection %d shape %d") for that problem. Obviously there's some overhead to switching contexts, even if no intervening messages are logged, but IMO it's a small price for a scheme that's convenient for programmers (and thus will be used) and useful for log-readers (because it provides the information needed without too much excess, and because the programmers actually use it - so it contains (and will contain more) useful information). The existing dprintf() and fdprintf() were forwarded to Logging, so all those existing log messages automatically come out in the new system (and can potentially benefit from context information), giving an immediate 'forced adoption'. ;) Anyway still, reducing the context-switching overhead is one area (of many) for improvement in the scheme. Oops, sorry, did not mean to get _that_ far off track from the original discussion of leveraging destructor calls for stack-based objects. But, I guess I'm me. :) Woody |
From: Br'fin <br...@ma...> - 2003-01-31 15:22:51
|
In general, I like the design I put forth for IFailable. It's intent was not to cover an object handled by several threads at once. The area I'm having problem is with with CError objects. As I work on it, I'm going lets add this and lets add that. And consequently it seems to be lacking a proper elegance to it. Marathon handles errors like so: The game hits an error and calls set_game_error(type, code) Type may be a gameError or a systemError. If it's a gameError, than code is limited to a particular subset of codes. If it's a systemError, than the particular error is recorded in the code. Code higher up the hierarchy can check if the game has an error_pending, it can read what kind of error is around with get_error and can clear the error. For instance, file finding code might report (systemError, fileNotFound) Further up the code sees this and says: Error pending? Ok, what is it? File not found? Ok, it's just the preferences, clear the error and continue on to create the file. For a contrasting viewpoint on errors, I looked at Java. That has the hierarchy of 'Throwable' leading to 'Exception' (things which your program should catch) and 'Error' (Things which your program shouldn't catch, they're just too big). And then further breakdowns of errors. In our case this would work out to one SystemException and then GameException with a hierarchy of specific exceptions beneath it. Another thing about Java exceptions is that the interface is fairly simple, You create a new exception and the string argument in the creator is optional. Hsm, was just thinking about exceptions and C++. Exceptions get to be a nuisance if the current function itself is just a pass through for an exception. (open_preferences would continue to throw a fileNotFoundException that originated in open_file and also had to propagate through wad_open.) Or do you just need to pussyfoot around your declarations to make sure that if you're passing an exception that you didn't declare any memory before the point where the exception can occur? -Jeremy Parsons |
From: Br'fin <br...@ma...> - 2003-01-31 00:38:52
|
On Thursday, January 30, 2003, at 07:02 PM, Dietrich Epp wrote: > > On Thursday, January 30, 2003, at 03:37 , Br'fin wrote: > >> Er, is which element hopelessly unthreadable? >> >> The current error handling. >> This error handling. >> Or? >> >> And I'd not mind knowing why. (I didn't think to take that >> possibility into account, so...) >> >> -Jeremy Parsons > > The bit about each object holding an error value. If two threads use > the same object, the error value won't work correctly. It also seems > that error data are not the property of an object itself, rather the > property of an aborted call, which is what exceptions are for. Hmm, unknown. I honestly hadn't been thinking of threads. My first instinct is 'what on earth do we have for objects that are accessed in multiple threads *AND* which are failable'. As for existing error handling, I was looking at game_errors.h/.cpp and some of how iostream type stuff does it. In the case of an iostream, the stream itself does get into bad states (The object is affected) A quick perusal of set_game_error (Current error handling) reveals the majority are around file handling (basic, wad, and preferences) and a couple netgame spots. (Inability to sync with others or server died) In the case of Marathon, the game itself goes into a bad state until falling far enough back in its main thread to correct it and/or report to the user. Admittedly, you do have a better argument for 'why exceptions' -Jeremy Parsons |
From: Dietrich E. <die...@zd...> - 2003-01-31 00:02:14
|
On Thursday, January 30, 2003, at 03:37 , Br'fin wrote: > Er, is which element hopelessly unthreadable? > > The current error handling. > This error handling. > Or? > > And I'd not mind knowing why. (I didn't think to take that possibility > into account, so...) > > -Jeremy Parsons The bit about each object holding an error value. If two threads use the same object, the error value won't work correctly. It also seems that error data are not the property of an object itself, rather the property of an aborted call, which is what exceptions are for. |
From: Woody Z. I. <woo...@sb...> - 2003-01-30 23:46:10
|
On Thursday, January 30, 2003, at 05:03 PM, Dietrich Epp wrote: > Um, isn't this hoplessly unthreadable? I'm sure Br'fin has something in mind for threading, perhaps using some sort of thread-local storage mechanism to hold an error state per-thread, or else embedding the Error objects themselves in various different places, each of which is only used by one thread, etc. So I would tend to think that answer is "no". :) But, it's something I hope we'll see addressed when he gets the time... it's not totally clear to me when and where Error objects actually live (that darned lack of context problem again...). Woody |
From: Br'fin <br...@ma...> - 2003-01-30 23:37:48
|
Er, is which element hopelessly unthreadable? The current error handling. This error handling. Or? And I'd not mind knowing why. (I didn't think to take that possibility into account, so...) -Jeremy Parsons On Thursday, January 30, 2003, at 06:03 PM, Dietrich Epp wrote: > > On Thursday, January 30, 2003, at 07:01 , Br'fin wrote: > >> Here is what I have blocked out for error type foo so far. Now I >> should mention that this is a model for error handling within a >> system. Compare and contrast with the current Marathon error >> handling system where an error occurs and it's all handled by >> various places checking the game's overall error state in >> game_error.h/.cpp >> >> Also, I think some of this might apply even if we were using >> exceptions to pass errors around. (Heck, the CError class itself >> that is forming would seem to make a nice throwable if we did go for >> exceptions) > > [snip] > > Um, isn't this hoplessly unthreadable? > |
From: Dietrich E. <die...@zd...> - 2003-01-30 23:03:39
|
On Thursday, January 30, 2003, at 07:01 , Br'fin wrote: > Here is what I have blocked out for error type foo so far. Now I > should mention that this is a model for error handling within a > system. Compare and contrast with the current Marathon error handling > system where an error occurs and it's all handled by various places > checking the game's overall error state in game_error.h/.cpp > > Also, I think some of this might apply even if we were using > exceptions to pass errors around. (Heck, the CError class itself that > is forming would seem to make a nice throwable if we did go for > exceptions) [snip] Um, isn't this hoplessly unthreadable? |
From: Michael A. <mdm...@ya...> - 2003-01-30 22:27:52
|
> > [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. > I have to agree with you on that one. I don't have every function call memorized. So if I see: foo(a); I assume 'a' won't be changed, but: foo(&a); Kind of lets me know a will be changed. In my opinion refs should only be const and used to avoid parameter copy costs for big structures. So: foo (&a); *Means* that a is meant to be changed, but a function of type: void foo(const Bar &a); is only doing the ref because Bar is really big and we don't want the efficiency cost of pass-by-value. And really who cares whether you type a->x vs a.x. Maybe if you had to do (*a).x sort of stuff, but that is rare. Just my opinion, Michael D. Adams mdm...@ya... __________________________________________________ Do you Yahoo!? Yahoo! Mail Plus - Powerful. Affordable. Sign up now. http://mailplus.yahoo.com |
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 |
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 |
From: Br'fin <br...@ma...> - 2003-01-30 16:56:23
|
On Thursday, January 30, 2003, at 10:47 AM, Woody Zenfell, III wrote: > I'm going to try to not get very involved with this one since you've > got other people actually making comments now ;) but one random > thought: Now now, all the input has been good. :) > Wouldn't it be good to have at least one of those 'int'-type fields be > 32 bits wide so that an error code returned by an existing API like > DirectX etc. could be stuffed in there? Maybe I'm missing some info > again... I suppose yeah every time a DirectX routine is called, the > caller should examine the error return and remap it to some sort of AM > error-code or something for consistency - but is anybody _really_ > going to do that? Probably having AM realize that the overall > operation failed - so it can tell the user or take corrective action - > and then spewing out the raw error code number into a log or something > is sufficient? You're probably right on the 32bit wide, I was just starting with the 16bit values that game_error.h was using. Which admittedly fits into the same sort of int size as a MacOS OSErr. Imagine that. Well, people should be checking the error codes of things to begin with. And to a certain degree, as long as error codes don't clash, the remapping could be to itself. Or be generic. It should at least be formatted into type='systemError', error_code='your sound card hates you', at which point a place for the actual platform specific code could indeed be good. > Also while I'm at it, I appreciate the idea of having a totally > standard calling convention etc., but isn't this > least-common-denominator approach really sort of more inconvenient > than it's worth? I mean, it sounds like a lot of things are just > going to return a pointer - is it really so bad to return a NULL on > failure and a good pointer on success? 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. I don't really see it as a least common denominator approach either. > 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 [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 > 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. > > But maybe I'm wrong. :) > 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'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. -Jeremy Parsons |
From: Woody Z. I. <woo...@sb...> - 2003-01-30 15:47:53
|
I'm going to try to not get very involved with this one since you've got other people actually making comments now ;) but one random thought: Wouldn't it be good to have at least one of those 'int'-type fields be 32 bits wide so that an error code returned by an existing API like DirectX etc. could be stuffed in there? Maybe I'm missing some info again... I suppose yeah every time a DirectX routine is called, the caller should examine the error return and remap it to some sort of AM error-code or something for consistency - but is anybody _really_ going to do that? Probably having AM realize that the overall operation failed - so it can tell the user or take corrective action - and then spewing out the raw error code number into a log or something is sufficient? Also while I'm at it, I appreciate the idea of having a totally standard calling convention etc., but isn't this least-common-denominator approach really sort of more inconvenient than it's worth? I mean, it sounds like a lot of things are just going to return a pointer - is it really so bad to return a NULL on failure and a good pointer on success? 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. 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. But maybe I'm wrong. :) Woody On Thursday, January 30, 2003, at 09:01 AM, Br'fin wrote: > class CError > { > private: > std::string error; > uint16 type; > uint16 error_code; |
From: Br'fin <br...@ma...> - 2003-01-30 15:01:15
|
Here is what I have blocked out for error type foo so far. Now I should mention that this is a model for error handling within a system. Compare and contrast with the current Marathon error handling system where an error occurs and it's all handled by various places checking the game's overall error state in game_error.h/.cpp Also, I think some of this might apply even if we were using exceptions to pass errors around. (Heck, the CError class itself that is forming would seem to make a nice throwable if we did go for exceptions) /////////////////////////////////////////////////////////////////////// // // $Id: CErrror.h $ /////////////////////////////////////////////////////////////////////// // /* * CErrror.h * AlephModular * * CError defines an interface for classes that can error an go into a * bad state. For instance, opening a file fails so you need to report * this back to the calling function. * * The basic method this supports is the following: * A class has a method this_may_fail that returns a boolean (If the * method needs to return something meaningful like an opened file * reference, then this is done with out arguments passed by * reference) If the method is successful, then it returns true. * Otherwise it returns false and puts the class into a failed state. * You may get a class's current state to report why an operation * failed. * * Created by Br'fin on Thu Jan 30 2003. * */ #ifndef __CERROR_H #define __CERROR_H #include "cseries.h" #include <string> class CError; /* * IFailable * Interface * * This is an interface to be supported by all classes that may fail. * For instance, opening a file may fail. * */ class IFailable { protected: /* * set_error * arguments: * an explanatory member of CError that explains the current error * the IFailable class is responsible for deleting this object */ virtual void set_error(const CError*) = 0; public: /* * is_good * returns: * true if and only if there is no error state on the object */ virtual bool is_good() const = 0; /* * get_error * arguments: * returns the objects's current error state. Do not free this * returned state or call clear_error before you use it. */ virtual void get_error(const CError* &) const = 0; /* * clear_error * This attempts to clear the error state of the object. This * is not guarunteed to succeed. If it does succeed then the * internal CError object is deleted. * returns: * true if the error can be cleared. False otherwise */ virtual bool clear_error() = 0; virtual ~IFailable() {} // Required virtual destructor }; class CError { private: std::string error; uint16 type; uint16 error_code; public: CError(std::string _reason, uint16 _type, uint16 _code) : error(_reason), type(_type), error_code(_code) {} CError(const CError& err) : error(err.error), type(err.type), error_code(err.error_code) {} void get_string(std::string& _string) { _string = error; } uint16 get_error_code() { return error_code; } uint16 get_error_type() { return type; } }; #endif |
From: Michael A. <mdm...@ya...> - 2003-01-30 13:36:37
|
--- Br'fin <br...@ma...> wrote: > > On Thursday, January 30, 2003, at 06:56 AM, Michael > Adams wrote: > > > --- Br'fin <br...@ma...> wrote: > >> By calling conventions I was thinking of calls > like > >> this: > >> > >> virtual bool get_named_child(const char *name, > >> IFileSystemDesc* > >> &out_desc) const = 0; > >> > > > > I thought you had mentioned useing STL strings > instead > > of char* in future code, but maybe I'm remembering > > wrong. Also did you really mean IFileSystemDesc* > > &out_sesc? That * with the & confuses me. Oh I > think > > I get it now. So you would call it like this: > > > > IFileSystemDesc *f = NULL; > > a->get_named_child("name", f); > > And the pointer f would now point to something > new? > > Yes, that was my idea when writing it out that way. > > > I haven't been paying much attention to this whole > > discussion, but why not do this? > > > > ... get_named_child(..., IFileSystemDesc > &out_desc); > > ... > > IFileSystemDesc f; > > a->get_named_child("x", f); > > > > It would make one less pointer floating around > which > > reduces chance of seg faults, but like I said I > > haven't been paying close enough attention so > maybe > > there is some reason for using a pointer that I'm > > missing. > > > The biggest problem with that is this is C++. So > IFileSystemDesc is an > interface. An Abstract Base Class. > > 'IFileSystemDesc f;' would result in a compile time > error (Trying to > instantiate an incomplete class) But even if it were > a full class, it > would be set in its ways. You can't really cram a > larger subclass into > IFileSystemDesc as it was allocated purely and > directly as a > IFileSystemDesc. > > -Jeremy Parsons > Yeah, your right. I forgot about that for a moment. Michael D. Adams __________________________________________________ Do you Yahoo!? Yahoo! Mail Plus - Powerful. Affordable. Sign up now. http://mailplus.yahoo.com |
From: Br'fin <br...@ma...> - 2003-01-30 12:29:12
|
On Thursday, January 30, 2003, at 06:46 AM, Michael Adams wrote: > > --- Dietrich Epp <die...@zd...> wrote: >> >> Is there really a difference between text and binary >> mode? I thought >> that was all arcane. I've never liked C++ >> iostreams, they have only >> served to add compile time and code size in the >> megabytes to my projects. > > Yes, there is a difference. I've had files > incorrectly read when the wrong one is used. Just > like ASCII vs. Binary FTP it can become a problem when > newlines are converted. This happens on at leat Win32 > because a \n in code becomes a \r\n in the file and > vise versa. I've seen quite a few bugs happen that > way. > On unix and Mac systems the bit of text vs binary is generally arcane. They may use different line endings, but they don't process that character on input/output. Oddly enough, the most common system, Windows, is the one that has a problem with it. Unless you specify a file to be binary when opening it, then there is a chance of having all your \n mishandled. (Expanding to \r\n when writing out, or munged together when reading in) -Jeremy Parsons |
From: Br'fin <br...@ma...> - 2003-01-30 12:23:34
|
On Thursday, January 30, 2003, at 06:56 AM, Michael Adams wrote: > --- Br'fin <br...@ma...> wrote: >> By calling conventions I was thinking of calls like >> this: >> >> virtual bool get_named_child(const char *name, >> IFileSystemDesc* >> &out_desc) const = 0; >> > > I thought you had mentioned useing STL strings instead > of char* in future code, but maybe I'm remembering > wrong. Also did you really mean IFileSystemDesc* > &out_sesc? That * with the & confuses me. Oh I think > I get it now. So you would call it like this: > > IFileSystemDesc *f = NULL; > a->get_named_child("name", f); > And the pointer f would now point to something new? Yes, that was my idea when writing it out that way. > I haven't been paying much attention to this whole > discussion, but why not do this? > > ... get_named_child(..., IFileSystemDesc &out_desc); > ... > IFileSystemDesc f; > a->get_named_child("x", f); > > It would make one less pointer floating around which > reduces chance of seg faults, but like I said I > haven't been paying close enough attention so maybe > there is some reason for using a pointer that I'm > missing. > The biggest problem with that is this is C++. So IFileSystemDesc is an interface. An Abstract Base Class. 'IFileSystemDesc f;' would result in a compile time error (Trying to instantiate an incomplete class) But even if it were a full class, it would be set in its ways. You can't really cram a larger subclass into IFileSystemDesc as it was allocated purely and directly as a IFileSystemDesc. -Jeremy Parsons |
From: Michael A. <mdm...@ya...> - 2003-01-30 11:56:31
|
--- Br'fin <br...@ma...> wrote: > By calling conventions I was thinking of calls like > this: > > virtual bool get_named_child(const char *name, > IFileSystemDesc* > &out_desc) const = 0; > I thought you had mentioned useing STL strings instead of char* in future code, but maybe I'm remembering wrong. Also did you really mean IFileSystemDesc* &out_sesc? That * with the & confuses me. Oh I think I get it now. So you would call it like this: IFileSystemDesc *f = NULL; a->get_named_child("name", f); And the pointer f would now point to something new? I haven't been paying much attention to this whole discussion, but why not do this? ... get_named_child(..., IFileSystemDesc &out_desc); ... IFileSystemDesc f; a->get_named_child("x", f); It would make one less pointer floating around which reduces chance of seg faults, but like I said I haven't been paying close enough attention so maybe there is some reason for using a pointer that I'm missing. Michael D. Adams mdm...@ya... __________________________________________________ Do you Yahoo!? Yahoo! Mail Plus - Powerful. Affordable. Sign up now. http://mailplus.yahoo.com |
From: Michael A. <mdm...@ya...> - 2003-01-30 11:46:28
|
--- Dietrich Epp <die...@zd...> wrote: > > On Wednesday, January 29, 2003, at 08:44 , Br'fin > wrote: > > > I've been mucking with a spare copy of AM, > actually trying to implement > > what I have described in my draft so far. Which is > of course turning up > > more areas to work upon. > > > > Like, most files are going to be opened in binary > mode. So this makes a > > reasonable default, however, some files in the > future are going to be > > text based, so we should support that as well. I > was thinking of > > exposing the iostream openmode flags as part of > the > > open_file_for_reading/writing business. And just > have it default to > > binary. You could clear it of course by passing 0 > for the mode (or a > > suitably equivalent definition) > > Is there really a difference between text and binary > mode? I thought > that was all arcane. I've never liked C++ > iostreams, they have only > served to add compile time and code size in the > megabytes to my projects. Yes, there is a difference. I've had files incorrectly read when the wrong one is used. Just like ASCII vs. Binary FTP it can become a problem when newlines are converted. This happens on at leat Win32 because a \n in code becomes a \r\n in the file and vise versa. I've seen quite a few bugs happen that way. Michael D. Adams mdm...@ya... __________________________________________________ Do you Yahoo!? Yahoo! Mail Plus - Powerful. Affordable. Sign up now. http://mailplus.yahoo.com |
From: Dietrich E. <die...@zd...> - 2003-01-30 09:58:31
|
On Wednesday, January 29, 2003, at 08:44 , Br'fin wrote: > I've been mucking with a spare copy of AM, actually trying to implement > what I have described in my draft so far. Which is of course turning up > more areas to work upon. > > Like, most files are going to be opened in binary mode. So this makes a > reasonable default, however, some files in the future are going to be > text based, so we should support that as well. I was thinking of > exposing the iostream openmode flags as part of the > open_file_for_reading/writing business. And just have it default to > binary. You could clear it of course by passing 0 for the mode (or a > suitably equivalent definition) Is there really a difference between text and binary mode? I thought that was all arcane. I've never liked C++ iostreams, they have only served to add compile time and code size in the megabytes to my projects. > The other two aspects I found myself leaning towards were calling > conventions and error feedback. > > By calling conventions I was thinking of calls like this: > > virtual bool get_named_child(const char *name, IFileSystemDesc* > &out_desc) const = 0; > > This is a method to specify a file or directory within a directory. The > name of that desc would be the string name. > > If the function was successful, then it would return true, and the > IFileSystemDesc* would point to a newly acquired class. (Which the > caller would need to free currently) > > If the function failed, then it would return false, and the > IFileSystemDesc* would remain untouched. > > And the reason for failure? Well, after a little bit of thinking, I > realized that there should be an IError interface with three methods. A > protected set_state, a public get_state and a public clear_state. And > in thinking of it, it's possible that the state itself could be verbose > enough to display/log when dumped to a stream. > > > Why not exceptions? I have not been sold on exception handling in C++ > under some warnings of exception handling being there, but less than > optimal under C++ (Less practical in terms of speed, doing extra work > with stuff too low level for me really to be thinking about :) ) and a > simple lack of knowledge on how one is supposed to do it in C++. (Java > is at least clear on what things you should be throwing around) > Admittedly a nice clear document that counters these perceptions as > myth might change my mind. Believe it or not, exceptions add no speed overhead. Well, there is overhead when they are thrown, but nobody cares how much overhead the throwing takes. Unfortunately, the exceptions that are part of the C++ libraries are rather lacking. It's a whole bunch of unnecessary abstraction in my mind. `new', for example, throws an exception when it allocates something. But the exception doesn't contain any more information than that, so it is not really helpful. You could make your own new, new[], delete, and delete[] that throw better exceptions. Basically the brain-damaged part of C++ to which you refer is that an exception can be anything. You can throw an integer [I wonder if you can throw a void?]. The remedy is that you just don't deal with it, because you won't be throwing them. Program by contract =) ---------- It seems to me that there is a better way to design these complicated file wrappers than putting all the functionality into one big mother interface. I have a few suggestions. Class: FileSystem - Manages the translation of paths (or other identifiers) into files - Locates files - Manages archives (in the future) FileHandle* OpenFile (const char* path, Mode mode); bool FileExists (const char* path); --- etc --- Class: FileHandle - Reads, writes, and seeks - Manages metadata, such as privileges, file size virtual size_t Read (void* buffer, size_t amount) = 0; virtual size_t Write (const void* buffer, size_t amount) = 0; --- etc --- The bit about archives is so you could distribute a scenario as a zip file or other format. The FileHandle is only an interface for reading and writing data, it could be subclassed to read data from an archive. You won't have to include any funky files, FileSystem manages which implementation is used. If the data itself is abstracted to a degree, the files that support mmap'ing could do so. This would provide performance benefits on systems with decent virtual memory such as OS X. I would be willing to "put my money where my mouth is" and design and implement the bits, if y'all are receptive to the idea... ----------- On a somewhat related note, it would be nice to choose a style for making AlephModular consistent, and maybe organize the files so you can actually find what you're looking for. |