I'm a C programmer learning C++. In C, there is a common goto
idiom used to handle errors and exit cleanly from a function. I've read that exception handling via try
-catch
blocks is preferred in object-oriented programs, but I'm having trouble implementing this paradigm in C++.
Take for example the following function in C which uses the goto
error handling paradigm:
unsigned foobar(void){
FILE *fp = fopen("blah.txt", "r");
if(!fp){
goto exit_fopen;
}
/* the blackbox function performs various
* operations on, and otherwise modifies,
* the state of external data structures */
if(blackbox()){
goto exit_blackbox;
}
const size_t NUM_DATUM = 42;
unsigned long *data = malloc(NUM_DATUM*sizeof(*data));
if(!data){
goto exit_data;
}
for(size_t i = 0; i < NUM_DATUM; i++){
char buffer[256] = "";
if(!fgets(buffer, sizeof(buffer), fp)){
goto exit_read;
}
data[i] = strtoul(buffer, NULL, 0);
}
for(size_t i = 0; i < NUM_DATUM/2; i++){
printf("%lu\n", data[i] + data[i + NUM_DATUM/2]);
}
free(data)
/* the undo_blackbox function reverts the
* changes made by the blackbox function */
undo_blackbox();
fclose(fp);
return 0;
exit_read:
free(data);
exit_data:
undo_blackbox();
exit_blackbox:
fclose(fp);
exit_fopen:
return 1;
}
I tried to recreate the function in C++ using the exception handling paradigm as such:
unsigned foobar(){
ifstream fp ("blah.txt");
if(!fp.is_open()){
return 1;
}
try{
// the blackbox function performs various
// operations on, and otherwise modifies,
// the state of external data structures
blackbox();
}catch(...){
fp.close();
return 1;
}
const size_t NUM_DATUM = 42;
unsigned long *data;
try{
data = new unsigned long [NUM_DATUM];
}catch(...){
// the undo_blackbox function reverts the
// changes made by the blackbox function
undo_blackbox();
fp.close();
return 1;
}
for(size_t i = 0; i < NUM_DATUM; i++){
string buffer;
if(!getline(fp, buffer)){
delete[] data;
undo_blackbox();
fp.close();
return 1;
}
stringstream(buffer) >> data[i];
}
for(size_t i = 0; i < NUM_DATUM/2; i++){
cout << data[i] + data[i + NUM_DATUM/2] << endl;
}
delete[] data;
undo_blackbox();
fp.close();
return 0;
}
I feel my C++ version didn't properly implement the exception handling paradigm; in fact, the C++ version seems even less readable and more error prone due to a build of cleanup code accumulating in the catch
blocks as the function grows.
I've read that all this cleanup code in the catch blocks may be unnecessary in C++ due to something called RAII, but I'm unfamiliar with the concept. Is my implementation proper, or is there a better way to handle errors and cleanly exit a function in C++?