Reputation: 13
When trying to encapsulate C API in C++ method, i found a problem in throwing an exception:
int status;
char* log = nullptr;
int infoLogLength;
getFooStatus(&status);
getFooLogLength(&infoLogLength);
if (!status) {
log = new char[infoLogLength];
getFooLog(infoLogLength, log);
throw std::runtime_error(log);
}
I am not allowed to modify the interface methods in any way.
From what i understand, i am required to reserve memory for the method to fill, and operate on that. However, throwing the exception will return from the method, not letting me to free the resources. Is my code correct, or should i go around this in some other way?
Upvotes: 1
Views: 116
Reputation: 597550
std:runtime_error
expects a std::string
, so give it a std::string
instead of a char*
:
int status;
getFooStatus(&status);
if (!status) {
int infoLogLength;
getFooLogLength(&infoLogLength);
std::string log(infoLogLength, '\0');
getFooLog(infoLogLength, &log[0]);
throw std::runtime_error(log);
}
Alternatively, you can pass a char*
, simply allocate it in a way that promoted auto-freeing, eg:
int status;
getFooStatus(&status);
if (!status) {
int infoLogLength;
getFooLogLength(&infoLogLength);
std::vector<char> log(infoLogLength);
getFooLog(infoLogLength, &log[0]);
throw std::runtime_error(&log[0]);
}
Upvotes: 2
Reputation: 58627
There is a simple way to deal with exceptions, without having to refactor the code so that the array is managed by an object whose destructor cleans it up. Namely:
char* log = nullptr;
try {
int status;
int infoLogLength;
getFooStatus(&status);
getFooLogLength(&infoLogLength);
if (!status) {
log = new char[infoLogLength];
getFooLog(infoLogLength, log);
throw std::runtime_error(log);
}
} catch (...) { // special C++ syntax: "catch any exception"
delete [] log;
throw; // special C++ syntax: "re-throw" current exception
}
If all you have is a single catch (...)
, it looks a lot as if C++ supports finally
. The difference betwen catch (...)
and a finally
feature is that catch (...)
does not execute unconditionally: it only executes if there isn't a more specific catch
. So, if you were to add additional catch clauses in the same try
block, they would all have to repeat the delete [] log
clean-up action.
This could be mitigated by using nesting:
try { // real exception-handling try
try { // try for unwind protection only
...
} catch (...) {
// clean-up statements
throw;
}
} catch (actual_error &err) {
...
} catch (another_exc_type &exc) {
...
}
Upvotes: 0
Reputation: 755
As far as I know runtime_error has two overloads, one with a const *char and another as const string&.
I believe constructing a std::string as a local variable and passing that to runtime_error should result in it being cleaned up properly.
Upvotes: 0