tags:

views:

109

answers:

4

I have this code to open multiple files one at a time that is given at the command line, and then if it cannot open one of the files, it closes all the files and exits.

/* Opens an array of files and returns a pointer to the first 
 * element (the first file).
 */
ifstream *OpenFiles(char * const fileNames[], size_t count)
{
    /* If no command line arguments, error and exit */
    if (count == 0) {
        cerr << "Invalid number of arguments.";
        exit(EXIT_FAILURE);
    }
    ifstream *fileObj;
    fileObj = new ifstream[count];

    if (fileObj == NULL) {
        cerr << "Failed to create space for files";
        exit(EXIT_FAILURE);
    }

    /* Opens one file at a time and closes all files if there is an 
     * error opening any file.
     */
    for (int loopCount = 0; loopCount < (int)count; loopCount++) { 
        fileObj[loopCount].open(fileNames[loopCount], ios::out); 
        if (!fileObj[loopCount].is_open()) { 
            cerr << "Failed to open " << fileNames[loopCount] << "\n";
            for (; loopCount >= 0; loopCount--) {
                fileObj[loopCount].close();
                cout << "Closed " << fileNames[loopCount] << "\n";
            }
            delete[] fileObj;
        } 
    }
    return fileObj;
}

I am doing this for homework and my teacher has another checker we have to submit to and gives me these types of warnings:

Assign8_1.cpp(44): error 445: (Warning -- Reuse of for loop variable 'loopCount' at 'line 40' could cause chaos)
    return fileObj;
Assign8_1.cpp(51): error 850: (Info -- for loop index variable 'loopCount' whose type category is 'integral' is modified in body of the for loop that began at 'line 40')
    return fileObj;
Assign8_1.cpp(51): error 449: (Warning -- Pointer variable 'fileObj' previously deallocated [Reference: file Assign8_1.cpp: lines 30, 48])
Assign8_1.cpp(30): error 831: (Info -- Reference cited in prior message)
Assign8_1.cpp(48): error 831: (Info -- Reference cited in prior message)
}
Assign8_1.cpp(63): error 818: (Info -- Pointer parameter 'files' (line 55) could be declared as pointing to const)
}

Starting with the first warning, I was wondering why I shouldn't use my loopCount variable twice the way I do in my code. That was the way I thought it would work, keeping track of which file I am looking at, opening, and closing it appropriately.

Does anyone know what error 449 means? Thanks.

+2  A: 

You absolutely can use your loopCount variable that way, but it'll cause you a bug. At cursory glance, it looks like it will run forever, because you never break out of the outer loop from the inner loop. You decrement loopCount in the inner loop, and increment it in the outer loop, back and forth, back and forth.

Though using a loopCounter in an inner loop isn't a violation of the language, it usually indicates something going on that isn't what you really intended.

You would be better off using code like this:

list<ifstream> files; 

ifstream file("name"); 
if ( file.is_open() ) { 
   files.push_back(file); 
}  

// close files 
for ( list<ifstream>::iterator i = files.begin(); iter++; i != files.end() ) { 
  i->close();   
}
Chris Kaminski
+4  A: 

You need to exit(EXIT_FAILURE) after you delete[] fileObj in the loop, otherwise you'll simply crash on the next iteration. That may be what warning 449 is telling you.

Other than that, the code looks fine. If you want it to compile without those warnings, though, you could turn the inner loop into a standard for-loop which only uses loopCount as a bound. Something like:

for (int i = loopCount; i >= 0; i--) {
    fileObj[i].close();
    cout << "Closed " << fileNames[i] << "\n";
}
Michael Myers
Better to do i=0 to i<=loopCount, because if you use an unsigned numeric type (such as std::size_t), it will wrap around and loop forever. It's a good habit to always go up.
Michael Aaron Safyan
Thanks for the tip, @Michael. I learned Java first, so I often forget to think of unsigned data types.
Michael Myers
A: 

449:

After you delete the array, you go round and start trying to open files again - if any of the open calls fail you'll be struck in the loop forever. You need either a return or a break after deleting the array.

Douglas Leeder
+1  A: 

I'd structure the code more like this:

for all input names
    if (!open file)
        break;

if (number of open files != number of input names) 
    close the files that did open
return (possibly empty) list of files
Jerry Coffin