1

I'm looking at error testing and reporting techniques from function calls, especially when multiple functions are called. As an example of what I mean, for simplicity each function returns a bool:

success = false;

if (fnOne ())
{
    if (fnTwo ())
    {
        if (fnThree ( ))
        {
            success = true;
        }
        else
        {
            cout << "fnThree failed" <<endl;
        }
    }
    else
    {
        cout << "fnTwo failed" <<endl;
    }
}
else
{
    cout << "fnOne failed" <<endl;
}

I find with the above example (which I see everywhere) the code quickly becomes unreadable, especially when it calling code becomes multi-screen in height.

Currently my way of dealing with this in C++ (Including 'c' tag in case someone has a C technique which is smooth) I store a bool and a string in my object. The bool represents success/fail and the string represents a reason for the fail state. I call a function and if the function fails, the function internally sets the object into fail state and provides a string based reason. I'm still not 100% happy with this method... but its the best I have so far. Example of what it looks like:

void myobj::fnOne (void)
{
    if (m_fluxCapacitorProngCount > 3)
    {
        setState (false, "myobj::fnOne - Flux capacitor has been breeding again");
    }
}

void myobj::fnTwo (void)
{
    if (m_answerToLifeUniverseAndEverything != 42)
    {
        setState (false, "myobj::fnTwo - Probability drive enabled?");    
    }
}

void myobj::setup (void)
{
    // Ensure time travel is possible
    if (valid())
    {
        fnOne ();
    }

    // Ensure the universe has not changed
    if (valid())
    {
        fnTwo ();
    }

    // Error? show the reason
    if (valid() == false)
    {
        cout << getStateReason () << end;
    }
}

Where valid () returns true/false and getStateReason () returns the string provided in the function when the error occured.

I like that this grows without the need to nest the conditions, to me I find this more readable but I'm sure there are problems...

What is the best [cleanest] way to handle detecting and reporting multiple function call return conditions?

2
  • You forget one variant (a variation of the first): bool r1 = fnOne(), r2 = fnTwo(); r3 = fnThree(); if (r1 && r2 && r3) { /* do something */ } else { /* do something else */ } Commented Mar 21, 2014 at 15:28
  • Exceptions are an option for C++ and I have seen this used (mostly badly unfortunately, spare you my horror story), not so for C (easily and cleanly) according to this: stackoverflow.com/questions/2891766/… Commented Mar 21, 2014 at 15:51

3 Answers 3

2

This code should be clearer than your first variant:

if (!fnOne ())
{
    cout << "fnOne failed" <<endl;
    return;
}
if (!fnTwo ())
{
    cout << "fnTwo failed" <<endl;
    return;
}
if (!fnThree ())
{
    cout << "fnThree failed" <<endl;
    return;
}
success = true;

In general, for C++ you can use exceptions for error handling.

Sign up to request clarification or add additional context in comments.

2 Comments

I like this approach actually and I realize how that this topic goes hand it hand with ones philosophy on whether one should use "return" in more that one place per function. I generally do not, because I have had lots of bad experiences which debugging code which is large, enter multiple levels of condition and then abruptly exit. Using return flat like this I think is decent
Perhaps it helps to differentiate between a state machine and error handling. Ask yourself the question: is it ok if fnOne fails? Can the program resume normally? If so perhaps your problem can benefit from using a state machine. Otherwise I would throw exceptions. But this is opinion based, so experiment and see what works in your scenario. Regarding indentation: in my opinion it is a good idea to never use more than two levels for any function.
0

If you really want one function to return a value that represents the success/failure of several other functions (and just that - not a generalized return value from each function, which would require some way of returning an array/tuple/vector of values), here's one approach:

int bigFunction()
{ int return_value = 0;

  if (function1() != 0)
    return_value |= (1 << 0);

  if (function2() != 0)
    return_value |= (1 << 1);

  if (function3() != 0)
    return_value |= (1 << 2);

  // ....

  return return_value;
}

The idea is to allocate one bit each in the return value to indicate success/failure of each sub-function. If your sub-functions have a small set of possible return values that you actually want to capture, you could use more than one bit per function - i.e. two bits would allow you four different values for that field.

On the other hand, something like this means you're probably either a) writing some pretty low-level code like a device driver or kernel or something or b) there is probably a better approach to solving the problem at hand.

Comments

0

Dealing with errors in your code (bugs) and errors arising out of user input is a huge topic on its own. The technique you employ depends on the complexity of your code and the expected life of the code. The error handling strategy you would employ for a homework project is less complex than the error handling strategy you would employ for a semester project, which will be less complex than the error handling strategy you would employ for an in-house project, which will be less complex than a project which will be widely distributed to clients.

Strategy 1: Write an error message and abort

The simplest error handling strategy, that you can employ in homework project, is write a message out to stdout and and then call abort().

void fun1(int in)
{
  if (in < 0 )
  {
    printf("Can't work with a negative number.\n");
    abort();
  }

  // Rest of the function.
}

Strategy 2: Set a global error code and return

The next level of error handling involves detecting a bad input and dealing with it without calling abort(). You could set a globally accessible error code to indicate the type of error. I would recommend using this approach for homework projects, semester projects, and projects that are exploratory in nature.

void fun2(int in)
{
  if (in < 0 )
  {
    // Indicate that "fun2" came accross a NEGATIVE_INTEGER_ERROR.
    setErrorCode(NEGATIVE_INTEGER_ERROR, "fun2");
    return;
  }

  // Rest of the function.
}

void funUser(int in)
{
  // Call fun2
  fun2(in);

  // If fun2 had any errors, deal with it.
  if (checkErrorCode())
  {
     return;
  }

  // Rest of the function.
}

The next level of error handling involves detecting a bad input and dealing with it using other options. You could return an error code from the function. If you are using C++, you could throw an exception. Both these options are valid ways of dealing with large projects --- be they in-house or distributed for wider consumption. They are applicable to any project in which the user base is beyond the team of developers.

Strategy 3: Return an error code from the function

int fun3(int in)
{
  if (in < 0 )
  {
    // Indicate that "fun3" came accross a NEGATIVE_INTEGER_ERROR.
    return NEGATIVE_INTEGER_ERROR;
  }

  // Rest of the function.
}

void funUser(int in)
{
  // Call fun3
  int ecode = fun3(in);

  // If fun3 had any errors, deal with it.
  if (ecode)
  {
     return;
  }

  // Rest of the function.
}

Strategy 4: Throw an error code from the function (C++)

void fun4(int in)
{
  if (in < 0 )
  {
    // Indicate that "fun4" came accross a NEGATIVE_INTEGER_ERROR.
    throw NEGATIVE_INTEGER_ERROR;
  }

  // Rest of the function.
}

void funUser(int in)
{
  // Call fun4. Be prepared to deal with the exception or let it be
  // dealt with another function higher up in the call stack.
  // It makes sense to catch the exception only if this function do
  // something useful with it.
  fun4(in);

  // Rest of the function.
}

Hope this gives you enough background to adopt an appropriate error handling strategy for your project.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.