5
votes

I have some code that looks as follows:

void MyClass::OnButtonClick()
{
    int retVal = SomeDialog.DoModal();
    if(retVal == MYCLASS_ERROR)
    {
        MessageBox("Error"...blah ...blah);
    }
    else if(retVal == IDOK)   // IDOK is returned on clicking the 'OK' button
    {
        MessageBox("All is well"...blah ...blah);
    }
}
  • SomeDialog just shows a progress bar. On any error, the progress bar is automatically closed by calling EndDialog(MYCLASS_ERROR). Only on successful completion will the user be allowed to click on 'OK' there.
  • MYCLASS_ERROR is a value in an enum which contains all sorts of return types and statuses.

I found that on clicking OK in SomeDialog, the error message still displayed! I dug a little deeper and found that MYCLASS_ERROR = IDOK = 1.

So my question is, how should I define all these return statuses such that it doesn't collide with any other implementation's statuses? Meaning, my functions should return values that is not returned by any other function (or as few other functions as possible).

I thought of modifying my design such that all functions only returned TRUE or FALSE. But, this would not be feasible in all cases. I have also searched quite a bit for answers and haven't found any thus far.

Thanks for looking!

3

3 Answers

3
votes

There are ways to work around this, but they're kind of ugly.

The cleanest design is usually the one that avoids clashing with standard Win32 idioms, and in the world of Win32, modal dialogs return one of the ID values to indicate the button that the user clicked when dismissing them (just like message boxes). Strictly speaking, it's the nResult parameter that was passed to the EndDialog function used to close the modal dialog box.

I recommend not trying to overload this return value with additional meaning. Trying to do so will just run you into trouble (for example, you probably also haven't noticed yet that a return value of -1 means that the creation of the dialog box failed).


Instead, define an additional member variable in your dialog class and use this to report your additional information. On success, you will return IDOK from the dialog. On failure (any kind of failure), return something like IDCANCEL. Your code inside of OnButtonClick would then check to see if the return value is IDOK or IDCANCEL. If it's IDCANCEL, then you also need to query the value of the member variable you added to the dialog for additional information.

Hopefully that made sense. If not, maybe this code example will (assume that m_errStatus is the member variable you added to your subclass of CDialog):

void MyClass::OnButtonClick()
{
    if (SomeDialog.DoModal() == IDOK)
    {
        // Success!
        // The OK button was clicked, so IDOK was returned.
        MessageBox("All is well"...blah ...blah);
    }
    else
    {
        // Failure!
        // Some error occurred, so IDCANCEL (or any other value) was returned.
        // Determine what to do now, based on additional information that the
        // dialog set upon failure.
        switch (SomeDialog.m_errStatus)
        {
            case MYCLASS_ERROR_1:
                MessageBox("Error 1 occurred.");
                break;
            case MYCLASS_ERROR_2:
                MessageBox("Error 2 occurred.");
                break;
            // etc.
        }
    }
}
1
votes

You could simply define your custom error codes to not "clash" with the Windows return values. Of course, you don't know when Microsoft will add new return values, so this will always be a little flakey.

You could try something like this:

enum MYERR
{
    MYERR_FIRST_ERROR   = 0x0F000000, /* large and unlikely to be used */
    MYERR_SECOND_ERROR,
    MYERR_THIRD_ERROR,
    /* and so on */
};

But, as Cody Gray notes above, I think you're better off returning a standard error code from DoModal, such as IDABORT instead of overloading the return codes like that. Then simply have a secondary error code that users must explicitly retrieve, which is your own "internal" error code.

0
votes

You can use enum instead of macro-defined return code, with namespace in order to avoid name-conflict. Look at the Boost Error Handling Policy :

namespace boost { namespace math { namespace policies {

enum error_policy_type
{
   throw_on_error = 0, // throw an exception.
   errno_on_error = 1, // set ::errno & return 0, NaN, infinity or best guess.
   ignore_error = 2, // return 0, NaN, infinity or best guess.
   user_error = 3  // call a user-defined error handler.
};

}}} // namespaces