4

What is a good way to return success or one or more error codes from a C++ function?

I have this member function called save(), which saves to each of the member variables, there are at least ten of these member variables that are saved-to, for the call to save(), I want to find out if the call failed, and if so, on which member variable (some are hard failures, some are soft).

2
  • Why do you need to provide multiple error codes? Perhaps if you give us more information about the usage pattern of such a function, the reason for multiple error codes, or whatnot, the perhaps someone has a better solution for you. Commented Jun 1, 2009 at 21:44
  • I have this member function called save(), which saves to each of the member variables, there are at least ten of these member variables that are saved-to, for the call to save(), I want to find out if the call failed, and if so, on which member variable (some are hard failures, some are soft). Commented Jun 1, 2009 at 21:49

9 Answers 9

7

You can either return an object that has multiple error fields or you can use 'out'parameters.

How you do this depends on your design and what exactly you are trying to return back. A common scenario is when you need to report back a status code along with a message of sorts. This is sometimes done where the function returns the status code as the return value and then returns the message status via an 'out' parameter.

If you are simply returning a set of 'codes', it might make more sense to construct a struct type and return that. In that case, I would be prone to pass it in as an out parameter and have the method internally update it instead of allocating a new one each time.

Are you planning on doing this once or many times?

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

1 Comment

An object is a nice solution as you can return essentially anything you want, e.g. error codes, error strings etc.
6

I usually use a boost::tuple:

typedef boost::tuple<int,int> return_value;

return_value r = my_function();

int first_value = boost::get<0>( r );
int second_valud = boost::get<1>( r );

EDIT

You can also use boost::tie to extract the values from a tuple:

boost::tie( first_value, second_value ) = r;

1 Comment

You can use tiers to ungroup the touple: boost.org/doc/libs/1_39_0/libs/tuple/doc/…
4

The simplest way to return two values is with the std::pair<> template:

Comments

3

I would use a bitset if you're intention is to purely return error states. e.g.

const bitset<10> a_not_set(1);
const bitset<10> b_not_set(2);
const bitset<10> c_not_set(4);

...

bitset<10> foo(T& a, T& b, T& c, ...)
{

    bitset<10> error_code = 0;

    ...


    if ( /* a can't be set */ )
    {
        error_code |= a_not_set;
    }

    ...

    if ( /* b can't be set */ )
    {
        error_code |= b_not_set;
    }

    ...

    // etc etc

    return error_code;
}

bitset<10> err = foo(a, b, c, ... );
if (err && a_not_set)
{
   // Blah.
}

Comments

2

You need to return them as output parameters:

 bool function(int& error1, int& error2, stringx& errorText, int& error3);

Comments

2

You can use an integer with bit manipulation (aka flags).

3 Comments

+1 Good idea for limited count of errors, maybe you could expand your answer with some code...
To solve the readability, you can always use bit fields which provides names for each flag, allows "normal" boolean operators instead of bit shift operators, and takes the same space as an int.
This is the pro way and it does not cost a thing in CPU time to implement.
2

I probably try to throw an exception first but it depends on your coding paradigm. Please check some books or articles about reasons why c++ exception handling might be better.

If I really need to stick to retrun-error-code style, I would define a eunm type for specifying errors with bit operations..

enum error
{
   NO_ERROR = 0,

   MEMBER_0_NOT_SAVED = 1,
   MEMBER_1_NOT_SAVED = 1 << 1,
   MEMBER_2_NOT_SAVED = 1 << 2,
   // etc..

};

int save()
{
    int ret = NO_ERROR;

    // fail to save member_0
    ret  |= MEMBER_0_NOT_SAVED;

    // fail to save member_1
    ret  |= MEMBER_1_NOT_SAVED;

    // ....

    return ret; 
}

int main(void)
{
    int ret = save();
    if( ret == NO_ERROR)
    {
       // good.
    }
    else
    {
        if(ret & MEMBER_0_NOT_SAVED)
        {
              // do something
        }

        if(ret & MEMBER_1_NOT_SAVED)
        {
              // do something
        }

        // check the other errors...
    }
}

This is just a rough example. It's better to put this into a class or use a namespace.

Comments

0

I am not familiar with the internals and constrains of your project, but if possible, try to use exceptions instead of error codes.

The reasons are listed here, at C++ FAQ lite, and they conclude with:

So compared to error reporting via return-codes and if, using try / catch / throw is likely to result in code that has fewer bugs, is less expensive to develop, and has faster time-to-market.

Comments

0

Vector should store multiple errors. Then by simplest way to check if vector is empty.

Example in godbolt: https://godbolt.org/z/TzhhEsGbf

#include <iostream>
#include <vector>
#include <string>

enum class ErrorCode {
    None,
    MemberVar1Error,
    MemberVar2Error
};

struct Error {
    ErrorCode code;
    std::string message;

    Error(ErrorCode code, const std::string& message)
        : code(code), message(message) {}
};

class MyClass {
public:    
    std::vector<Error> save() {
        std::vector<Error> errors;

        if (!saveMemberVar1()) {
            errors.emplace_back(ErrorCode::MemberVar1Error, "Failed to save memberVar1");
        }
        if (!saveMemberVar2()) {
            errors.emplace_back(ErrorCode::MemberVar2Error, "Failed to save memberVar2");
        }

        return errors;
    }

private:
    bool saveMemberVar1() {
        // Simulate a failure
        return false;
    }

    bool saveMemberVar2() {
        return true;
    }
};

int main() {
    MyClass myObject;

    if (auto errors = myObject.save(); !errors.empty()) {
        std::cout << "Save operation failed with the following errors:\n";
        for (const auto& error : errors) {
            std::cout << "Error: " << static_cast<int>(error.code) << " - " << error.message << "\n";
        }
    } else {
        std::cout << "Save operation succeeded\n";
    }

    return 0;
}

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.