vanta mula
vanta mula

Reputation: 141

How to return error code from constructor?

I am trying to return error code from constructor, since constructor does not return an error code, I tried to put an exception on the constructor. Then in the catch block I return my appropriate error code. Is this a proper way of returning error code from constructor ?

#include <exception>
#include <iostream>

class A {
 public:
  A() { throw std::runtime_error("failed to construct"); }
};

int main() {
  try {
    A a;
  } catch (const std::exception& e) {
    std::cout << "returining error 1 \n";
    return 1;
  }

  return 0;
}

Upvotes: 9

Views: 17276

Answers (5)

Igor Levicki
Igor Levicki

Reputation: 1596

There is nothing preventing you from returning an error code from a constructor by throwing an exception.

You just need to derive your own exception class from std::runtime_error:

#include <stdexcept> // for std::runtime_error
#include <windows.h> // for HRESULT

class com_error : public std::runtime_error
{
    HRESULT hr;
public:
    com_error(const char *_Message, const HRESULT _hr)
    : std::runtime_error(_Message)
    , hr(_hr)
    {
    }

    HRESULT error_code() const noexcept
    {
        return hr;
    }
};

Now you can throw an exception which contains error code:

class A
{
public:
    A()
    {
        HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
        if (SUCCEEDED(hr)) {
            // TODO: continue initialization
        } else {
            throw com_error("COM initialization failed", hr);
        }
    }
};

And you can catch it and examine the error code:

try {
    A *a = new A();
    // ...
} catch (com_error& e) {
    HRESULT hr = e.error_code();
    // TODO: Do what you want with error code here
}

That doesn't mean it is a good idea. It is better to use Initialize / Shutdown pattern when the object construction is complex and might require cleanup / rollback.

Upvotes: 0

Joe
Joe

Reputation: 655

By adding a layer of abstraction over your constructor, you can achieve your goal of checking to see if a constructor has failed and return an error code. By using a Initialize-Shutdown pattern, you can ensure that if an error occurs somewhere in your constructor, you'll still have access to your object to cleanup anything that you allocated. Here's an example:

// This model we can potentially throw an exception from the constructor
class Keyboard {
private:
    Device* mDevice;
public:
    Keyboard() { 
        mDevice = ConnectToDevice();
        mDevice->Init(); // Say this can throw exception } // If exception thrown, the destructor is not called and memory is leaked
                                                          // The object no longer exists, no way to clean up memory

    ~Keyboard() { DisconnectFromDevice(mDevice); } // DisconnectFromDevice() cleans up some dynamically allocated memory
};

// In this model we use a Initialize-Shutdown pattern
class Keyboard {
private:
    Device* mDevice;
    bool IsInitialized = false;
public:
    Keyboard() {} 
    ~Keyboard() { DisconnectFromDevice(mDevice); } // DisconnectFromDevice() cleans up some dynamically allocated memory
    void Initialize() {
        mDevice = ConnectToDevice();

        if (this.mDevice == nullptr)
            status = -1;

        mDevice->Init(); // Caller still needs to catch exception if it throws
                        // If exception is thrown here, the caller is responsible for cleaning up
                        // However, the object is still alive so caller can manually call or other cleaning method
        
        IsInitialized = true;
        
        return;
    }
    void Shutdown() {
        if (IsInitialized)
            DisconnectFromDevice(mDevice);

        return;
    }
};

Upvotes: 2

MikeMB
MikeMB

Reputation: 21156

Depends on how likely you deem such an error and how critical a proper initialization is for further program execution.

  • If a failure to initialize the object is considered an exceptional case (e.g. because you ran out of memory) then throw an exception.
  • If it is an expected failure with wich you want to deal right here and now, set the object into a failed/default constructed state and allow the user to query for the error code (e.g. like when std::fstream isn't able to open a file). This is especially helpful if you want to retry the object initialization with different parameter values.

Btw.: If you decide on using an exception, I'd probably not turn it into an error code unless you absolutely must. Exceptions where specifically designed, such that you don't have to pass error codes around manually.

Upvotes: 0

HatsuPointerKun
HatsuPointerKun

Reputation: 637

According to isocpp.org, the proper way to handle the failure in a constructor in C++ is to :

Throw an exception.

It's not possible to use error code since constructors don't have return types. But :

If you don’t have the option of using exceptions, the “least bad” work-around is to put the object into a “zombie” state by setting an internal status bit so the object acts sort of like it’s dead even though it is technically still alive.

But you should really use exceptions to signal failure in constructors if you can, as said :

In practice the “zombie” thing gets pretty ugly. Certainly you should prefer exceptions over zombie objects, but if you do not have the option of using exceptions, zombie objects might be the “least bad” alternative.

Upvotes: 12

Derek Kaplan
Derek Kaplan

Reputation: 138

There is no way to do this. The best way would probably be to have a static init() method that would return an instance of the class and make the constructor private. You could do most of the constructing from the init method, and just return an error code from that.

Upvotes: 1

Related Questions