31

I'm using error codes for handling errors in my c++ project. The problem is how to return error codes from a function which is supposed to return some variable/object.

consider this:

long val = myobject.doSomething();

Here, myobject is an object of some class. If doSomething function encounters some error condition then how should it notify the caller (Without using exceptions).

Possible solutions:

  1. Have a data member (say err_) in the class which can be checked by the caller. But it would be unsafe in a multi-threaded application sharing the same object and calling the same function.
  2. Use some global error variable, again same issue in a multi-threaded environment.

Now how can I notify the caller about some error condition?

Jeet
  • 1,030
  • 2
  • 10
  • 20
  • Related: [Convention result and code error C++ int foo (…)](http://stackoverflow.com/questions/1882047/convention-result-and-code-error-c-int-foo) – C. Ross Jul 01 '10 at 10:59
  • 12
    You've just discovered the #1 reason for exceptions, BTW. But why are you also rejecting the other common solution to reporting errors, which is to return an error code and use out-parameters for function results? – MSalters Jul 01 '10 at 11:10
  • @MSalters I'm not using the other approach you mentioned because some of the code is already done and that would add to inconsistency in the style – Jeet Jul 01 '10 at 12:35
  • 1
    If you need to return an object, and you don't want to pass this by non-`const` reference, and there is no obvious error value or you need to indicate what specifically went wrong, then you either need exceptions or things are going to become clumsy. That's just the way it is. `+1` to MSalters comment from me. – sbi Jul 01 '10 at 13:05

13 Answers13

33

Make a template called, say, Maybe that it parametrized by your return value type. Whenever you return a value, wrap it in this template like this:

Maybe<long> result = object.somemethod();

The Maybe template would have a way of being instantiated with an error code (probably a static method):

return Maybe<long>::error(code);

But ordinarily would just be returned with the value:

Maybe<long> retval;
retval = 15;
return retval;

(You would have to, of course, override the appropriate constructors, assignment operators, etc.)

In the client side you call a method to check for the error.

Maybe<long> result = object.somemethod();
if (result.is_error) 
{ 
    ... handle the error ...
}
else
{
    ... use the result ...
}

Again you'd need the appropriate operators defined to use Maybe<long> wherever there's a long required.

This sounds like a lot of work, but really the work is done once in making a good, bulletproof Maybe template. You'll also have to do some performance tuning on it to avoid nasty overheads. If you want to make it more flexible you can parametrize it on both the return value type and the error type. (This is only a minor increase in complexity.)

JUST MY correct OPINION
  • 35,674
  • 17
  • 77
  • 99
  • 7
    +1 - This is pretty elegant. You can extend it nicely by adding a cast operator to `bool` so you can write `if (result)` instead of `if (!result.is_error)` and add an exception on accessing the result value if the result is an error. I know that tossing an exception is what we are avoiding but this would really be a good case for it. – D.Shawley Jul 01 '10 at 11:18
  • 2
    I would say accessing the value when it is an error state is an ideal situation for tossing an exception, actually, since it shows obvious programmer error: not checking error returns. – JUST MY correct OPINION Jul 01 '10 at 11:19
  • @JUST: And if you think this just a bit further, you'll arrive at throwing exceptions right away instead of relying on programmers on checking return values. – sbi Jul 01 '10 at 13:08
  • 6
    As I said elsewhere on this page, exceptions are for exceptional circumstances (read: unexpected situations). Return codes are for when you expect errors as part of normal program execution. You don't use exceptions as a general purpose signalling mechanism. [Look at the first item on this list](http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.12) for some guidance. Exceptions are not return codes and should never be used as a general return code mechanism. Using them as such obfuscates code in the worst `COME FROM` kind of way **and** costs a lot. – JUST MY correct OPINION Jul 01 '10 at 13:56
  • Ah, Haskell :) IMHO, it is a great solution, but for a different problem. Your template, or Boost optional type are best used in situations where a function may or may not return a result: quering a collection is a classic example of that. – Nemanja Trifunovic Jul 01 '10 at 14:02
  • 1
    Actually, @Nemanja Trifunovic, if I were to do this seriously in code I'd do an `Either` template (which I alluded to at the end of my answer there) but the `Maybe` monad is a more recognizable name to casual Haskell users. – JUST MY correct OPINION Jul 01 '10 at 15:28
  • 3
    It's called `boost::optional` and `boost::variant`, the runtime cost of those boost library is nearly negligible, because the values you put there are stack allocated: the templates wrap a flag indicated which value is currently stored + a `union`-like structure (maybe raw-memory correctly aligned depending on the current object). – Matthieu M. Jul 08 '10 at 17:04
  • @Matt: I'm not just being paranoid about performance; I've run into cases where more complex return types cause big slowdowns. It sounds like using a `boost::optional` or `boost::variant` would yield some flexibility, but would likely prevent optimizations such as simply returning the value in the accumulator. Whether this is an acceptable cost is something we'd have to decide after measuring it in context, of course. The alternative, which I've suggested elsewhere, is to use a thread-local errno. – Steven Sudit Jul 08 '10 at 17:08
  • @Steven Sudit: I'm certainly sorry if you thought I downreguarded your concern for performance, I certainly wasn't. `variant` has been benchmarked to be as fast as a structure holding an enum + a union, while `boost::any` is on the order of magnitude of `dynamic_cast`ing. I don't know any benchmark for Boost.Optional unfortunately, so I can't answer here. – Matthieu M. Jul 08 '10 at 17:28
  • @Matt: No, the error is mine if I made it seem like I was offended. I'm not, at all. It's good to know that `boost::variant` is fairly cheap while `boost::any` is a bit more expensive. I suspect that `boost::optional` would be comparable in performance with the former. – Steven Sudit Jul 08 '10 at 17:40
9

You probably want something like Alexandresu's Expected<T> idiom.

Christopher Smith
  • 5,372
  • 1
  • 34
  • 18
  • 6
    Can you summarize the 1.5-hour long talk here? Is `Expected` basically a`Variant` with a `T` or `std::exception_ptr`? – mic Nov 20 '20 at 03:43
  • 1
    Writing a summarise with minimum example is more helpful, anyway thanks to share :) – S.M.Mousavi Sep 17 '22 at 13:11
8

You can pass variable as reference and return error code in it.

anthares
  • 11,070
  • 4
  • 41
  • 61
7

You can return a std::pair holding both an error code (or error object) and the desired return object. The object of interest needs a default constructor or value so you can return something even when an error is encountered.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
4

It is common to return a return/error code, and make available a property or member with the results.

int retCode = myobject.doSomething();
if (retCode < 0){ //Or whatever you error convention is
   //Do error handling
}else{
   long val = myobject.result;
}

It is also common to pass in a pointer that is set to the return value, and return the return/error code. (See HrQueryAllRows).

long val = INIT_VAL;
int retCode = myObject.doSomething(&val);

if (retCode < 0){
    //Do error handling
}else{
    //Do something with val...
}
C. Ross
  • 31,137
  • 42
  • 147
  • 238
4

You have three options:

  • Create a class containing the return value and a possible error code.

  • Use something like boost::optional for the return value, which allows for invalid responses.

  • Pass a reference to a variable and return any possible error code within that.

bfx
  • 897
  • 10
  • 16
Conor
  • 1,028
  • 1
  • 8
  • 15
4

I see there are many nice solutions, but I approach it in another way if I have this situation.

auto doSomething()
{        
    // calculations
    return std::make_pair(error_code, value)
}

int main()
{
    auto result = doSomething();
    if (!result.first)
    {
        std::cout << result.second;
    }
    else
    {
        std::cout << "Something went wrong: " << result.second;
    }
}

For me it's a clean solution than passing bool as reference. auto return type deduction is supported from c++14

iM71
  • 301
  • 2
  • 6
2

Return an error handle. Have an error manager keep the error codes and additional informations (e.g. ERROR_INVALID_PARAMETER and name-value-pairs like ParameterName="pszFileName"). This information can be accessed using the handle. The caller can check the error handle against a NO_ERROR_HANDLE. If true, no error occurred. The caller can augment the error information and pass the handle up the stack. The error manager can be one for the process or one for each thread.

ur.
  • 2,890
  • 1
  • 18
  • 21
2

The most common practice is to return the error code

long result;
int error = some_obj.SomeMethod(&result);

or return a value that indicate there was an error:

long result = some_obj.SomeMethod();
if (result < 0) error = some_obj.GetError();
adf88
  • 4,277
  • 1
  • 23
  • 21
  • 1
    The `GetError()` method really limits your ability to write quality concurrent code (you're saving off the state from a method call in your object). – weberc2 Mar 29 '15 at 20:15
2

I would suggest following:

class foo {
public:
    long doSomething();
    long doSomething(error_code &e);
};

Where error_code is some type that holds error. It may be integer or better something based on boost::system::error_code.

And you supply two functions:

  1. First version throws the error, for example throw boost::system::system_error that is created from boost::system::error_code.
  2. Second returns the error code into e.
Artyom
  • 31,019
  • 21
  • 127
  • 215
2

In C++17 you use std::optional from the <optional> header:

std::optional<long> myobject = some_func(some_bool);
if (myobject.has_value()) {
    // do stuff with myobject.value()
} else {
    // myobject has no value
}

// ...

// Example function that returns an optional
std::optional<long> some_func(bool b) {
    if (b) 
        return 15;
    return {};
}

Offtkp
  • 366
  • 2
  • 9
  • Using `optional` to signal failures is generally [not recommended](https://www.boost.org/doc/libs/1_67_0/libs/optional/doc/html/boost_optional/tutorial/when_to_use_optional.html#boost_optional.tutorial.when_to_use_optional.not_recommended_usages), since it lacks a way to report failure details. – Alex Che Oct 14 '22 at 22:47
0

define all the error codes in a File. based on error category you can return the error code and the caller can decide what went wrong and caller can return its own error code.

for example

#define FILE_ERROR        1
#define SANITY_ERROR      2

int WriteToFile(char* Data, int iErrorCode)
{
   char* FileName;

  if (!FileOpen(FileName, &iErrorCode))
  {
     //Analyze error code and make decision on what to ignore or terminate
     iErrorCode = FILE_ERROR;
     return 0;
  }

}

int FileOpen(char* FileName, int* iErrorCode)
{

    if (FileName == null)
     {
       iErrorCode = SANITY_ERROR;
      return 0;
    }

    ///// next code blocks
    return 1;
}
Maximilian Ast
  • 3,369
  • 12
  • 36
  • 47
Sagar
  • 1,115
  • 2
  • 13
  • 22
0

I found a new way to do it. It is non-standard and this is an entirely new way to do it. So consider using this approach cautiously.

Use the following header file:

SetError.h:

#include <string> // for string class 



#ifndef SET_ERROR_IS_DEFINED
#define SET_ERROR_IS_DEFINED

class Error {

public:
    int code = 0;
    std::string errorMessage;
    std::string fileName;
    std::string functionName;

    Error() {}

    Error(int _errorCode, std::string _functionName = "", std::string _errorMessage = "", std::string _fileName = "")
    {
        code = _errorCode;
        functionName = _functionName;
        errorMessage = _errorMessage;
        fileName = _fileName;
    }
};

#if defined(_DEBUG) || !defined(NDEBUG) 
#define ___try { _ERROR.code = 0; bool __valid_try_mode_declared; 
#define ___success }
#define SetError(pErrorData) __valid_try_mode_declared = true; _ERROR = *pErrorData; delete pErrorData;
#else
#define ___try { _ERROR.code = 0;
#define ___success }
#define SetError(pErrorData) _ERROR = *pErrorData; delete pErrorData; 
#endif

#endif

inline Error _ERROR;

Include it everyware.

Example of how to use:

Main.cpp:

#include "SetError.h"
#include <iostream>


bool SomeFunction(int value) ___try; 
{ 


    if (value < 0) {
        SetError(new Error(10, "SomeFunction", "Some error", "File main.cpp"));
        return false;
    }

    return true;
} ___success; // You mast to warp the function with both ___try and ___success
// These keywords must be at the start and the end of the function!




int main()
{
    using namespace std;

    bool output = SomeFunction(-1);

    if (_ERROR.code != 0) { // This is how you check the error code. using the global _ERROR object
        cout << "error code: " << _ERROR.code << ", from function: " 
            << _ERROR.functionName << ", from file: " << _ERROR.fileName;
    }

    cout << endl << "Founction returned: " << output << endl;

    return 1;
}

If you have some functions that run in another thread, these functions need to be inside namespace and then you can do this:

namespace FunctionsInSomeThread
{
    #include "SetError.h"

    bool SomeFunc1() ___try;
    {
        SetError(new Error(5, "SomeFunction2", "Some error from another thread", "File main.cpp"))
        return true;
    } ___success;

    bool SomeFunc2() ___try;
    {
        SetError(new Error(5, "SomeFunction2", "Some error from another thread", "File main.cpp"))
            return true;
    } ___success;

}

And to access _Error, you need to add the namespace of the thread

if (FunctionsInSomeThread::_ERROR.code != 0)
{
    // Error handling
}

Or in case it is inside the same namespace then no need to add FunctionsInSomeThread:: before.

The idea behind this is that you can't warp the function only with ___success; keyword. You will get compile error. So the developer will never return old error code from another function.

If you wrote ___success; at the end of the codeblock, you must write also ___try; at the start! You also can't use SetError macro if it is not wrapped in ___try; and ___success;.

The idea come from the AutoIt language where you have this consept: https://www.autoitscript.com/autoit3/docs/functions/SetError.htm

So this is almost the same in C if you use this header.

gil123
  • 512
  • 6
  • 12
  • Not really new; it just reinvents `errno`, with totally unnecessary dynamic allocation to boot. Also it is not exception-safe or thread-safe. – Lightness Races in Orbit Apr 25 '19 at 12:45
  • Sorry, I did not know about `errno`. Is it allow you to return also message in addition to only error code? – gil123 Apr 25 '19 at 12:56
  • It does not mean to be an exception. exceptions are for exceptional circumstances. About the claim that it is not thread-safe, it depends on how you use it. If all the code that runs in another thread is inside namespace and you include it in also *inside* that namespace, then another _Error object will be created for this thread and any use of SetError macro within that namespace will access that specific _Error object inside this thread. – gil123 Apr 25 '19 at 13:17
  • I didn't say it was an exception; I said it's not exception-safe. Though honestly I flinch when people reject the use of exceptions for this use case with the phrase "exceptions are for exceptional circumstances" - that's exactly what an error condition should be, and they're perfect for this job. What is the practical problem with using them, besides the phrase "exceptions are for exceptional circumstances"? – Lightness Races in Orbit Apr 25 '19 at 13:19
  • Regarding threads, typically you cannot say that all functions that will run on one thread are in a distinct namespace. Often we run the same functions in multiple threads, concurrently. A person would have to be _very_ careful taking this approach in a multi-threaded application. – Lightness Races in Orbit Apr 25 '19 at 13:22
  • Also you have undefined behaviour as [`_Error` is a reserved word, as are `__try` and `__success`](https://stackoverflow.com/a/228797/560648). – Lightness Races in Orbit Apr 25 '19 at 13:24
  • Sorry, this really is just a reimplementation of exceptions but with all the limitations of old `errno` ;) – Lightness Races in Orbit Apr 25 '19 at 13:25
  • I am happy also to find out that I reinvented something without knowing that it exists. For me it is a way to know that may idea perhaps is a good idea because someone had to think about it. I fixed to code to remove unnecessary allocations in release mode. I added something that will prevent the developer to use SetError macro when he did not use `___try;` and `___success;` keywords. – gil123 Apr 25 '19 at 14:08