How to return error code from constructor?

Viewed 12541

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;
}
5 Answers

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;
    }
};

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.

Related