1

I want to have a function which dynamically creates and returns a 2D array or when memory allocation fails passes the exception without information loss after cleaning up already allocated rows:

double **create (int rows, int cols)
{
    double **array = new double* [rows];
    for (int x=0; x<rows; x++)
    {
        try { array[x] = new double [cols]; }
        catch (exception &e)
        {
            x--;
            for (; x>=0; x--)
                delete[] array[x]; // clean up already allocated rows
            delete[] array;
            throw e; // pass exception
        }
        for (int y=0; y<cols; y++)
            array[x][y] = 0; // initialize array
    }
    return array;
}

So I can be sure, if create throws, there is no memory leak. But can I be sure, that the passed exception e is "the same" as if directy thrown by new and not catched?

E.g.

int main ()
{
    double **d;
    try { d = create (HUGE_x, HUGE_y); }
    catch (exception &e)
    {
        // 1. e could be thrown by new double* [rows]
        //      e.g. if HUGE_x is already to huge
        // 2. e could be thrown by throw e
        //      e.g. if after some rows no memory anymore
        // in both cases: is e the same?
    }
    return 0;
}

Or is it necessary to have catch (bad_alloc &e) inside the create function? Or does it work only with catch (...) { /* do clean-up*/ throw; }? Is there the same problem as in C# with losing stack trace when re-throw is not with throw; simply?

And another, more general question:

void f () { throw Object(); } // or throw "help";

void main ()
{
    try { f(); }
    catch (Object &e) // or catch (char *)
    {
        // Where is the Object or C-String created on the stack in function f()
        // since we aren't any more in function f() but we are dealing with
        // references/pointers to a non-existent stack?
    }
}
BenMorel
  • 34,448
  • 50
  • 182
  • 322
mb84
  • 683
  • 1
  • 4
  • 13

2 Answers2

4

For exception-safe memory management, use RAII. Rather than juggling raw pointers and exception handlers, assign the resource to a class which will release it on destruction. That way, everything is cleaned up automatically if an exception is thrown.

In this case, std::vector is a suitable RAII class managing a dynamic array:

vector<vector<double>> create (int rows, int cols) {
    return vector<vector<double>>(rows, vector<double>(cols));
}

(Note that it may be more efficient to represent the 2D array as a single array of size rows*cols, with accessors to provide 2D indexing into it. But that's off-topic for this question, so I won't go into tedious detail).

To answer your questions, although they're largely irrelevent if you write exception-safe code:

But can I be sure, that the passed exception e is "the same" as if directy thrown by new and not catched?

It won't be; you're throwing a new object, created by copying or moving e, with type exception.

Or is it necessary to have catch (bad_alloc &e) inside the create function?

Then you won't catch other types of exceptions. In this case, that might not a problem, but you really want to be catching all exceptions if you're going to be cleaning up like this. To reiterate: don't use exception handlers to clean up. It's very error-prone.

Or does it work only with catch (...) { /* do clean-up*/ throw; }?

That will rethrow the original object, which is what you want. (Except that you shouldn't want to be catching anything in the first place).

Is there the same problem as in C# with losing stack trace when re-throw is not with throw; simply?

Standard exceptions don't give you a stack trace anyway. If you have a custom exception type with a stack trace, then it depends on whether it generates a new one when copied/moved, or copies/moves the existing one.

Where is the Object or C-String?

The exception handling mechanism creates it somewhere (not on the stack which is about to be unwound), and destroys it once it's been handled. It's not specified exactly where it is, just how it must work.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • `vector` isn't the best way to create 2D array – dynamic Nov 22 '13 at 16:41
  • @llnk: Perhaps not, but it's a minimal change from the `double**` in the question. I might add a note about alternatives, but that's getting rather off-topic. – Mike Seymour Nov 22 '13 at 16:42
  • Yes, it would be interesting if you provide good alternative to vector of vector for 2d array (other than having a single vector of rows*cols) – dynamic Nov 22 '13 at 16:44
  • The sad truth is that even with C++11 there is nothing good for matrix in C++ built-in – dynamic Nov 22 '13 at 16:47
  • @MikeSeymour: it's an alternative, but not an answer to my questions. And 2D and not 1D with rows*cols, because I'm doing spatial convolution on this arrays (which represent images), and I think that *(*(array+x)+y) may be quicker than *(array+cols*x+y) and anyway I like more the optic array[x][y] (and I don't mind the extra double[rows] of memory). – mb84 Nov 22 '13 at 17:06
  • @mb84: It may not answer your questions, but writing intrinsically exception-safe code makes them irrelevant. I'll see if I can answer them anyway. – Mike Seymour Nov 22 '13 at 18:10
  • @mb84: OK, I've answered your questions about the nuances of exception handling, if you really want to be messing around catching and rethrowing instead of writing exception-safe code in the first place. – Mike Seymour Nov 22 '13 at 18:21
  • @MikeSeymour: nice answer, thank you very much. I will use the `catch(...){throw;}` solution. My program is forced to deal with exceptions, because of third party lib. So latest in `main()` I will catch all exceptions and `exit()` program with appropriate output and having freed all memory before exit (even if OS would do it anyway). So program is in fact exception-safe. – mb84 Nov 22 '13 at 19:07
0

While this question is quite old by now and has been answered adequately, I would like to add a note on rethrowing exceptions. In standard C++11 you can retain the original exception information by rethrowing with:

std::nested_exception and std::throw_with_nested


FYI: using this, you can also generate exception backtraces, as described on StackOverflow here and here, without need for a debugger or cumbersome logging, by simply writing a proper exception handler which will rethrow nested exceptions.

Since you can do this with any derived exception class, you can add a lot of information to such a backtrace! You may also take a look at my MWE on GitHub, where a backtrace would look something like this:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"
GPMueller
  • 2,881
  • 2
  • 27
  • 36