Prev: Deriving from a class derived from a CDialog
Next: How to show underlined menu shortcut letters by default for a contextmenu ?
From: Joseph M. Newcomer on 23 Jun 2010 14:57 Good observations, all! joe On Wed, 23 Jun 2010 17:39:12 +0200, Giovanni Dicanio <giovanniDOTdicanio(a)REMOVEMEgmail.com> wrote: >On 23/06/2010 17:39, Hector Santos wrote: > >> Another problem is that constructors do not lend itself for functional >> programming and exceptions are required, if necessary for the class logic. > >Note that there is an alternative design: two-step construction. >(This is widely used in MFC as well, e.g. CWnd default constructor >basically puts the object in a safe state, but then you need to call >CWnd::Create/CreateEx). > >With two-step construction you don't need to throw exception in ctor, >because the default ctor just puts the object in a safe state (e.g. zero >the pointers, etc.) and the actual construction is done in a /ad hoc/ >construction method (e.g. Init(), Create()...). > >Note also that if you throw an exception in the ctor, the destructor is >*not* called! So, to write exception-safe code, you should pay attention >to this point and provide proper cleanup code in ctor as well. >This is bad, IMHO. >But this problem doesn't occure in two-step construction: in fact, the >default ctor doesn't throw, and if the Init()/Create() construction >method throws, the destructor is properly called. > >Moreover, you can't call (or is it not safe to call...) virtual methods >in a ctor; but you can call virtual methods in an Init()/Create() >method, after the default ctor put the object in a safe state. > >I like the two-step construction pattern for C++, and I listed above >some reasons (even if the C++-FAQ discourages its use). > > >Giovanni Joseph M. Newcomer [MVP] email: newcomer(a)flounder.com Web: http://www.flounder.com MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Hector Santos on 23 Jun 2010 15:58 That is why I say the real issue is the lack of documentation or the "hidden" knowledge that are wrapped into classes. When you use fopen(), you know what the possible errors are, basically invalid file path, error 2 not found, for a already exist mode, error 3 read/write sharing issue, error 5 or 32 but regardless of the error code, the #1 idea is that the FILE * stream variable is NULL. Some of the things I am coming across with .NET is that some old traditional and SOLID concept no longer apply. For example, opening a file with append mode. Traditionally, the file is open/creating with a FILE SHARE READ/WRITE mode. This is not the case any more with .NET. The StreamWriter constructor: StreamWriter(string filename, bool Append) opens the file in FileShare.Read mode only. Whats funny is that I asked this question in MS FORUMS and someone came back suggestion that 95% scenarios this is expected usage and I represent 5% scenarios. I suggested that for C/C++ developers migrating to .NET, it would be a higher expectation that opening a file or stream in append mode is atomic and Read/Write Share mode simply because that is what they are use to. Anyway, my point here is that in C/C++ I don't need an exception trap for fopen() based appending which I had to add in .NET to find out what was going on, and after I found out and changed the code so open the file first in FileShare.ReadWrite mode and pass this to StreamWrite, I decided to keep the trap in to cover other possible sceanario, like OUT OF DISK space :) -- Giovanni Dicanio wrote: > On 23/06/2010 01:51, Joseph M. Newcomer wrote: > >> Note that this could be handled as >> >> BOOL TryParse(...args...) >> { >> try >> { >> Parse(...); >> return TRUE; >> } >> catch(..whatever..) >> { >> return FALSE; >> } >> } >> >> It ain't rocket science. And it isn't clear to me how handling the >> exception results in >> "bad code". > > Sure it isn't (ain't?) rocket science... but do you like a fopen that > throws an exception if the file cannot be opened? No, I prefer one > returning an error code. > The fact that you can't open a file is not an exceptional condition, and > I prefer code like: > > if ( some_open_file_api(...) == error ) > { > ... do what you want... (e.g. create the file, or other stuff...) > } > ... normal flow > > instead of try/catch. > > It was clearly written before: the usefulness of exceptions is inversely > proportional to the number of try/catch you use: if you clutter your > code with lots of try/catch then IMHO you are overusing (abusing) > exceptions. > > I think exceptions should be used in *exceptional* conditions (like > David wrote before). > > > BTW: I like these articles from the OldNewThing blog: > > "Cleaner, more elegant, and wrong" > http://blogs.msdn.com/b/oldnewthing/archive/2004/04/22/118161.aspx > > "Cleaner, more elegant, and harder to recognize" > http://blogs.msdn.com/b/oldnewthing/archive/2005/01/14/352949.aspx > > > Giovanni > -- HLS
From: Joseph M. Newcomer on 23 Jun 2010 15:08 One of the colossal failures of FP was that in its early incarnataions, it was "side-effect-free" code, which makes you wonder why you would ever want to bother executing it at all. When FP was coming into style, it was all about having no side effects, which meant that if your code ran for a year, it had the same effect as if it had not run at all, so you could save a lot of computer time by typing it in and never executing it. Haskell and other modern FP languages have a complex matrix of side effects, from the "unsafe" writes-to-a-file types to the pure functions. I have always been somewhere in the middle; a function should either be executed solely for its side effects (changes in the program's internal state or data's external state, such as a file, the Registry, etc.) or it should be truly pure functional code, with all "side effects" happening solely to its parameter values (e.g., CString &) or expressed in its return result. I also believe that when external state is affected, that the behavior must be transactional, that is, either it writes to the file or it does not write to the file, but it NEVER writes partial information to the file (and we can debate what should happen on "disk full" errors). I'm less of a stickler for parameter effects, and I'm willing to accept "If this function returns TRUE, then the CString & contains the result, but if this function returns FALSE, the CString & contents are not defined", and I would never pass in other than a temporary CString reference in such a case (upon returning TRUE, I might "commit" that result to the internal program state by doing an assignment, but I would never pass in a direct reference to the program state CString). Methods which interact do so through member variables of the class that contains them, and this state is invisible outside the class (it must always be, at worst, "protected", and maybe even "private") Therefore, I see exceptions as a way of executing something of the form {wp() f() assert(oc)} where wp() is the weakest preconditions tor successful execution of f, f does the computation, and oc is the set of output predicates that must be true. Thus, I view an exception as something limited to the {} above, and which can be thrown by wp(), by f(), or by the assert(oc), but which always transfers control out of the {} set. In the pure FP model, an exception terminates the top-level function. joe joe On Wed, 23 Jun 2010 14:49:30 -0400, Hector Santos <sant9442(a)nospam.gmail.com> wrote: >Good Point Giovanni. > >Off hand, as you mentioned this, my mind is thinking the Two step >approach was common with Borland Pascal Oops or POOP as I use to call >it. <g> > >But I never really understood why a FP approach was not a big >consideration for construction: > > SomeClass *sc = new SomeClass(); > if (sc == NULL) { > //some constructor error occurred > } > >I guess you could do a macro, template or something if you wanted this. > >-- > >Giovanni Dicanio wrote: > >> On 23/06/2010 17:39, Hector Santos wrote: >> >>> Another problem is that constructors do not lend itself for functional >>> programming and exceptions are required, if necessary for the class >>> logic. >> >> Note that there is an alternative design: two-step construction. >> (This is widely used in MFC as well, e.g. CWnd default constructor >> basically puts the object in a safe state, but then you need to call >> CWnd::Create/CreateEx). >> >> With two-step construction you don't need to throw exception in ctor, >> because the default ctor just puts the object in a safe state (e.g. zero >> the pointers, etc.) and the actual construction is done in a /ad hoc/ >> construction method (e.g. Init(), Create()...). >> >> Note also that if you throw an exception in the ctor, the destructor is >> *not* called! So, to write exception-safe code, you should pay attention >> to this point and provide proper cleanup code in ctor as well. >> This is bad, IMHO. >> But this problem doesn't occure in two-step construction: in fact, the >> default ctor doesn't throw, and if the Init()/Create() construction >> method throws, the destructor is properly called. >> >> Moreover, you can't call (or is it not safe to call...) virtual methods >> in a ctor; but you can call virtual methods in an Init()/Create() >> method, after the default ctor put the object in a safe state. >> >> I like the two-step construction pattern for C++, and I listed above >> some reasons (even if the C++-FAQ discourages its use). >> >> >> Giovanni >> Joseph M. Newcomer [MVP] email: newcomer(a)flounder.com Web: http://www.flounder.com MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Joseph M. Newcomer on 23 Jun 2010 15:12 In fact, you can argue that RegExError is actually a list of exceptions RegExSyntaxError RegExMatchFailure RegExInternalError and possibly others. I need to know WHY the regex failed. For example, in the FreeBSD code there are a whole lot of different syntax errors (bad pattern syntax, ^ not appearing on left of pattern, $ not appearing on right of pattern, extra ')', missing ')', etc.) and a collection of fialures during pattern matching, all of which require different responses from the user. In my implementaiton, I split up the parsing of the regexp from the execution of the match and the replacement of the patterns, and can handle errors for each of them separately. joe n On Wed, 23 Jun 2010 17:44:22 +0200, Giovanni Dicanio <giovanniDOTdicanio(a)REMOVEMEgmail.com> wrote: >On 23/06/2010 17:39, Hector Santos wrote: > >> public bool SearchForums(string sPattern) >> { >> try >> { >> >> Regex rgx = new Regex(sPattern, RegexOptions.IgnoreCase); >> foreach (var forum in ForumsList) >> { >> MatchCollection matches = rgx.Matches(forum.Description); >> if (matches.Count > 0) >> { >> /// got something >> } >> } >> return true; >> } >> catch (Exception ex) >> { >> MessageBox.Show("Regular Expression Error: " + ex.Message); >> } >> return false >> } >> >> This turned out to be nice > >catching(Exception) in C# is like catch(...) in C++: basically you are >swallowing everything... but IMHO you should only process Regex related >exception. > >I don't know the details of .NET Regex, but I think they should have >designed an /ad hoc/ exception class for regex (e.g. RegexError), so >that you could selectively catch RegexError and not swallow unrelated >exceptions. > >Note also that C# exceptions are better designed than C++ ones. >For example, you can put localized strings in C# exceptions, it is >possible to nest C# exceptions (see e.g. Exception.InnerException >property) to provide better diagnosis, etc. > >Giovanni Joseph M. Newcomer [MVP] email: newcomer(a)flounder.com Web: http://www.flounder.com MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Joseph M. Newcomer on 23 Jun 2010 15:12
I used strsafe.h in VS6 for many years, and it worked quite well. joe On Wed, 23 Jun 2010 10:58:12 -0400, "RB" <NoMail(a)NoSpam> wrote: > >> Ugh. Can't you get a more recent compiler? VC6 - ugh! > >Yea, I can relate to your frustration from a teaching standpoint. >And to infuriate you more, I do now own VC 2005 Pro but have >not used it much. I am developing all functional apps on the 2005 >but still use the VC6 for experimenting because I am so used to >it. Actually other than it's total inability to do a lot of STL items >and lack of strsafe items, I actually like it. But yes I must and >will move on to 2005 totally soon. > >> About DEL_ON_EXIT: I made it for myself a long time ago, >> .........Here it is: > >Thanks, I'm going put this into VC and step thru it a few times >so I can get the full impact of it. >Later...........RB Joseph M. Newcomer [MVP] email: newcomer(a)flounder.com Web: http://www.flounder.com MVP Tips: http://www.flounder.com/mvp_tips.htm |