C++ Error Handling – Implementing RAII with C API and Exceptions

api-designcc++11exceptionsraii

I have an API written in C, which produces a result by returning a pointer to allocated memory.
For using it with C++ (C++11) I've wrapped the function calls in objects, which keep the result in a std::shared_ptr. So far so good.

However, the C library features two functions for every operation. One produces possibly an error, the other never. Let's call them
some_pod * do_it_with_error(Parameter ..., Error **)
and
some_pod * do_it_without_error(Parameter ...)

I can pass in the address of an Error * pointer to the first function and if there's an error it won't be NULL afterwards.

For solving this I thought of two different implementations.
First, I could SFINAE for choosing between the with_error and without_error functions, like so:

template<typename NoThrow = void>
struct do_it {
  public:
    void operator()(...)
    {
      Error * error = NULL;
      m_result = std::shared_ptr<some_pod>(do_it_with_error(..., &error));
      if (error) {
        throw(MyExceptionWithError(error));
      }
    }
  private:
    std::shared_ptr<some_pod> m_result;
};

template<>
class do_it<std::nothrow_t> {
  public:
    void operator()(...) noexcept
    {
      if (! m_result) {
        m_result = std::shared_ptr<some_pod>(do_it_without_error(...));
      }
    }
  private:
    std::shared_ptr<some_pod> m_result;
};

Usage:

do_it<> di_error;
try {
  di_error(...); // might throw
} catch (...) {}

do_it<std::nothrow_t> di_no_error;
di_no_error(...); // won't throw for sure

However, since the result is wrapped in a std::shared_ptr there will also be
a get() method:

const std::shared_ptr<some_pod> & get(void) { return m_result; }

For error checking I could just implement a another method check.
The default implementation would now look like this:

struct do_it {
  public:
    // will never throw
    const std::shared_ptr<some_pod> &
    get(void) noexcept
    {
      if (! m_result) {
        m_result = std::shared_ptr<some_pod>(do_it_without_error(...));
      }
      return m_result;
    }

    // might throw
    do_it &
    check(void)
    {
      if (! m_result) {
        Error * error = NULL;
        m_result = std::shared_ptr<some_pod>(do_it_with_error(..., &error));
        if (error) {
          throw ...;
        }
      }
      return *this;
    }

  private:
    std::shared_ptr<some_pod> m_result;
};

Usage:

do_it di_error;
try {
  auto result = di_error.check().get();
} catch (...) {}
do_it di_no_error;
di_no_error.get();

So, both implementations seem to be equally good (or bad) to me.
How could I decide which one to use.
What are the Pros and Cons?

Best Answer

Too complicated - wrap them both as if either could throw an error, that the 2nd fn will never throw only means the compiler will never have to call the error handling routines (and might even optimise it out entirely) - either way your code will perform just as well thanks to C++s design.

Your code will also be much easier to understand, even with this redundant error handling (that one day might be used for other, unexpected errors)

Related Topic