RFC on an approach to address the "return boolean or throw exception" quandary

This is a discussion on RFC on an approach to address the "return boolean or throw exception" quandary within the C++ Programming forums, part of the General Programming Boards category; Originally Posted by kmdv So don't define it as an enum with predefined "bad" code (multiple names for the same ...

  1. #16
    Registered User gardhr's Avatar
    Join Date
    Apr 2011
    Posts
    151
    Quote Originally Posted by kmdv View Post
    So don't define it as an enum with predefined "bad" code (multiple names for the same thing just shocked me...).
    I'm not sure I follow you there. Do you mean only define a successful state? Or what?

    The multiple defines are nothing really unusual; some prefer the "good"/"bad" naming convention, others "success"/"failure" (or simply "fail"). I was just accomodating the most common choices.

    Quote Originally Posted by kmdv View Post
    I consider it a bad usage, not a taste.
    How so? Because it doesn't "feel right"? Okay, so rather than shame those who prefer the functor syntax, suggest an additional named function. Being that the error class only has "setters" for functions that take arguments, it's perfectly logical to opt for a functor syntax; it's clean, simple, and unambiguous, I think.

    Quote Originally Posted by kmdv View Post
    It's not about technical problems (which could be removed with new usage of the 'explicit' keyword) it is just confusing. For example, implicit conversion to bool for shared pointers is obvious, since C++ allows such for raw pointers.
    What's so confusing about an "if(error)" idiom? Or equating an error with a code? Assuming the user has RTFM before using the class, there shouldn't be any confusion anyway. And again, a named function for those who prefer it is reasonable as well, so both the "explicit intent" and "syntactic sugar" camps could be accommodated, anyway.

    Quote Originally Posted by kmdv View Post
    It's undefiend behaviour, there is no discussion. And I certainly wouldn't call it a practical way.
    Agreed, it wasn't the best solution, but I already suggested a reasonable alternative (ie: the "shared handle" route). And by "practical" I simply meant that there weren't any other obvious techniques at my disposal. I certainly welcome any suggestions, of course.

    Quote Originally Posted by kmdv View Post
    Because you are not going to catch every single kind of exception. Base class should have a virtual getter to allow derieved classes to reimplement their own ones.
    Okay, good point. Maybe it'd be a good idea to derive the class from std::exception, too?

    Quote Originally Posted by kmdv View Post
    Just because it is defined and it compiles does not mean it is good.
    Admittedly, I hadn't really thought enough about it to grasp the true implications there. A few simple test cases were enough to convince me, though, that the current usage is unacceptably unsafe. Namely, instatiating an error object directly is what can wreak so much havoc. Unfortunately, there's really no way to make the constructors private/protected without rendering the class unusable, so the only viable option that I can see would be to demand that the user either check or record the return value at the call site or else catch the exception. Direct instantiation would be forbidden and the consequence undefined behaviour.

    Quote Originally Posted by kmdv View Post
    By that I meant that this usage is very unintuitive.
    Can't please everyone, I suppose...

    Quote Originally Posted by brewbuck View Post
    Other cool tricks you can do with this...

    Templatize it so it wraps a generic error code (int, enum, whatever), instead of limiting to just bool.

    Give the ability to discard an error code deliberately without checking it. You can ignore an error, but you need to call a function to explicitly do it.

    Give the ability to detect positive vs. negative status, for instance you can trigger a breakpoint whenever an error is first generated, but continue along quietly when things are going fine.

    Make the whole thing controlled by a compile-time flag. When disabled, the return codes all revert back to plain old int/bool/enum again. Now your debug builds can have really sweet error tracing, and your release builds don't suffer from it.

    Remember that this is all great for debugging, but not at all suitable for an error code architecture.
    Great suggestions! Could you please elaborate on the third comment, though? I'm not exactly sure what you meant by that.
    Last edited by gardhr; 01-05-2012 at 02:05 PM.

  2. #17
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,235
    Quote Originally Posted by gardhr View Post
    Great suggestions! Could you please elaborate on the third comment, though? I'm not exactly sure what you meant by that.
    Suppose you're writing a library with some API called UsefulFunction():

    Code:
    int UsefulFunction() // returns 0 on success, negative on fail
    {
        DoSomeStuff();
        int status = SomeInternalFunction();
        if (status < 0)
            return status;
        DoSomeOtherStuff();
        return 0;
    }
    A user of the library calls UsefulFunction() and it returns -1 (something bad happened). Now it is your task to debug this failure. Well, the failure must have come from SomeInternalFunction(). But why did THAT fail? Maybe SomeInternalFunction() calls functions X(), Y(), and Z(), all of which can fail, and you don't know which. Maybe it was Z() that failed. But Z() calls A() and B()... Etc.

    This is a headache which usually requires a lot of repetitive stepping to figure out exactly what FIRST went wrong to trigger the cascade of error codes.

    Now imagine you are using a status class like the one you've written here. You could put some code into the constructor and assignment operator which checks to see if the raw error code is negative -- if it is, you can trigger a breakpoint.

    With that in place, you can reproduce the failure case, and the code will immediately break at the lowest layer where the error was actually generated in the first place. No detective work required.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  3. #18
    Registered User
    Join Date
    Aug 2010
    Location
    Poland
    Posts
    681
    Quote Originally Posted by gardhr View Post
    I'm not sure I follow you there. Do you mean only define a successful state? Or what?

    The multiple defines are nothing really unusual; some prefer the "good"/"bad" naming convention, others "success"/"failure" (or simply "fail"). I was just accomodating the most common choices.
    When you look at code it is not obvious whether the type of success equals the type of good. You are also not sure whether these two have the same integral value.

    Quote Originally Posted by gardhr View Post
    How so? Because it doesn't "feel right"? Okay, so rather than shame those who prefer the functor syntax, suggest an additional named function. Being that the error class only has "setters" for functions that take arguments, it's perfectly logical to opt for a functor syntax; it's clean, simple, and unambiguous, I think.
    There are many things that you prefer now but which you will find later a total mess. Yes, it doesn't feel right. One or two setters would be more appropriate, but I would even consider removing this feature.

    Quote Originally Posted by gardhr View Post
    What's so confusing about an "if(error)" idiom? Or equating an error with a code? Assuming the user has RTFM before using the class, there shouldn't be any confusion anyway. And again, a named function for those who prefer it is reasonable as well, so both the "explicit intent" and "syntactic sugar" camps could be accommodated, anyway.
    I wasn't precise about that (it was related to my previous post where I talked about the conversion to int) and the bool example misleaded you. Conversion to bool is fine, I meant the conversion to int.

    Quote Originally Posted by gardhr View Post
    Agreed, it wasn't the best solution, but I already suggested a reasonable alternative (ie: the "shared handle" route). And by "practical" I simply meant that there weren't any other obvious techniques at my disposal. I certainly welcome any suggestions, of course.
    The whole m_throws_ mechanism is suspicious to me. If you are going to modify the state of the object being copied, it is not a solution. Using the mutable keyword is also NOT the solution. m_throws_ is a part of object's state and such an object should be passed by a non-const reference in case you want to modify it.

    Quote Originally Posted by gardhr View Post
    Okay, good point. Maybe it'd be a good idea to derive the class from std::exception, too?
    You could derieve from runtime_error, which implements the what() member function. It has its pros and cons. The main idea is to have one base for all (or at least as many as possible) application-specific exceptions.

    I would like to add something about your naming convention, which obviously follows the one from STL - ugly underscores, abbreviations, confusing getters/setters (overloading), etc.
    It is a matter of style of course, however, camelCase (or CamelCase, which I prefer) is seen much more often in projects, I guess.
    Last edited by kmdv; 01-06-2012 at 03:36 AM.
    I never put signature, but I decided to make an exception.

  4. #19
    Registered User
    Join Date
    Aug 2010
    Location
    Poland
    Posts
    681
    I couldn't edit:

    Quote Originally Posted by kmdv View Post
    The whole m_throws_ mechanism is suspicious to me.
    Yes, shared state would be a reasonable solution.
    I never put signature, but I decided to make an exception.

  5. #20
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,235
    You can debate hypotheticals and const-correctness if you want. However, last year I used a mechanism essentially the same as this to track down a library error in a customer integration remotely, in minutes. I'm glad it worked, as I would have had to hop on a flight to Bangalore I it didn't.

Page 2 of 2 FirstFirst 12
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. example from c programming "a modern approach"
    By convenientstore in forum C Programming
    Replies: 8
    Last Post: 06-15-2009, 03:08 AM
  2. Exceptions "try, catch, and throw Statements" ???
    By Loic in forum C++ Programming
    Replies: 2
    Last Post: 08-12-2008, 09:22 PM
  3. Replies: 5
    Last Post: 06-06-2007, 11:10 PM
  4. "CWnd"-"HWnd","CBitmap"-"HBitmap"...., What is mean by "
    By L.O.K. in forum Windows Programming
    Replies: 2
    Last Post: 12-04-2002, 06:59 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21