qn regarding file closure problem

ok my lecturer is away and I need some help so here goes:

Q1) in the constructArray() function, why is it that I have to do file.clear() ( after the reading part ) before I can file.close() successfully, what caused the file stream to be in an error state and how do I solve this?

Q2) another thing is that when I try to pass a character constant to an array of a function, I get a compiler warning as below, since this conversion is deprecated, what would be the non-deprecated alternative to it?
lab10.cpp: In function 'int main()':
lab10.cpp:85: warning: deprecated conversion from string constant to 'char*'
lab10.cpp:86: warning: deprecated conversion from string constant to 'char*'
lab10.cpp:93: warning: deprecated conversion from string constant to 'char*'
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#include<iostream>
#include<iomanip>
#include<fstream>
#include<ctime>
#include<cstdlib>

//global declarations
using namespace std;

//constants
const int MAX = 100;
const int WIDTH = 20;

//user defined types
enum NumType { Odd, Even };

//structure definitions
struct Number
{
    int no;
    NumType type;
    int oddDigits;
    int evenDigits;
    int sumDigits;
    int noDigits;
};

//function prototypes
void constructInfile( fstream&, char [] );
int constructArray( fstream&, char [], Number [] );
void processArray( Number [], int );
void arrayToOutfile( fstream&, char [], Number [], int );
void numProperties( int, NumType&, int&, int&, int&, int& );
void checkFileStream( fstream&, char [], char [] );

//constants


int main()
{
    //local declarations
    fstream file;
    Number array[MAX];
    int size;

    //statements
    srand( time( NULL ) );

    //input
    constructInfile( file, "infile.txt" );
    size = constructArray( file, "infile.txt", array );

    //computations
    processArray( array, size );


    //output
    arrayToOutfile( file, "outfile.txt", array, size );


    return 0;
}

void constructInfile( fstream& file, char fileName[] )
{
    //local declarations
    int noOfEntries = rand() % MAX + 1;

    //statements
    cout << "Attempting to create file: " << fileName << "...";
    cout << endl;

    file.open( fileName, ios::out );

    checkFileStream( file, "create", fileName );

    for ( int idx = 0; idx < noOfEntries; idx++ )
        file << rand() << endl;

    file.close();

    return;
}

int constructArray( fstream& file, char fileName[], Number array[] )
{
    //local declarations
    int size = 0,
        temp;

    cout << "Attempting to open file: " << fileName << "...";
    cout << endl;

    file.open( fileName, ios::in );

    checkFileStream( file, "open", fileName );

    while ( file >> temp )
    {
        array[size].no = temp;
        size++;
    }

    if ( file.eof() )
        cout << "Data read successfully" << endl;
    else
        cerr << "Reading failed" << endl;

    file.clear();
    file.close();

    checkFileStream( file, "close", fileName );

    return size;
}

void processArray( Number array[], int size )
{
    for ( int idx = 0; idx < size; idx++ )
    {
        numProperties( array[idx].no,
                   array[idx].type,
                   array[idx].oddDigits,
                   array[idx].evenDigits,
                   array[idx].sumDigits,
                   array[idx].noDigits
                 );
    }

    return;

}

void numProperties( int num, NumType& oddEven, int& oddCount, int& evenCount, int& sumDigits, int& noDigits )
{
    int temp;

    oddCount = evenCount = sumDigits = noDigits = 0;

    if ( num % 2 )
        oddEven = Odd;
    else
        oddEven = Even;


    while ( num > 0 )
    {
        temp = num % 10;

        sumDigits += temp;
        if ( temp % 2 )
            oddCount++;
        else
            evenCount++;
        noDigits++;

        num /= 10;
    }

    return;
}

void arrayToOutfile( fstream& file, char fileName[], Number array[], int size )
{
    cout << "Attempting to create file: " << fileName << "...";
    cout << endl;

    file.open( fileName, ios::out );

    checkFileStream( file, "create", fileName );

    file << left;

    file << setw(WIDTH) << "No";
    file << setw(WIDTH) << "Type";
    file << setw(WIDTH) << "Odd";
    file << setw(WIDTH) << "Even";
    file << setw(WIDTH) << "Sum";
    file << setw(WIDTH) << "Digit";
    file << endl;

    for ( int idx = 0; idx < size; idx++ )
    {
        file << setw(WIDTH) << array[idx].no;

        switch ( array[idx].type )
        {
            case Odd: file << setw(WIDTH) << "Odd";
                  break;
            case Even: file << setw(WIDTH) << "Even";
                   break;
        }

        file << setw(WIDTH) << array[idx].oddDigits;
        file << setw(WIDTH) << array[idx].evenDigits;
        file << setw(WIDTH) << array[idx].sumDigits;
        file << setw(WIDTH) << array[idx].noDigits;
        file << endl;
    }

    file.close();

    checkFileStream( file, "close", fileName );

    return;

}

void checkFileStream( fstream& file, char operation[], char fileName[] )
{
    if ( file.fail() )
        cerr << "failed to " << operation << " " << fileName << endl;
    else
        cout << operation << " " << fileName << " successful" << endl;


    return;
}
The loop:

1
2
3
4
5
  while ( file >> temp )
    {
        array[size].no = temp;
        size++;
    }


essentially loops until the stream fails to input, correct?

When that happens, the stream is put into an error state.

What do you know about stream operations on a stream in an error state?

when stream is in an error state, no more input can be input until the state is restored to a working state, I do not know about how this affects file closure though, care to share some light?
so how do I make it such that this won't happen?
I'm not sure if the stream will refuse to close the file for the same reason or not. My first impression was that since you were asking it did, but on second thought the destructor has to close the file regardless, so I'm not sure, and I don't want to tell you wrong.

In general, in writing robust programs, it is impossible (or at least bad practice) to write code under the assumption that no errors can occur while reading the file. Therefore, I would suggest that you focus not on avoiding the problem, but rather on recovery when the problem occurs.
Closure of file failed when I didn't include the clear() originally, it was just a hypothesis on my part that the file failed to close if the stream is in an error state that turned out to be right because file closed successfully upon my addition of clear().

So in other words, my current code is alright?
what are the ways I can optimize it and what are the bad habits I need to improve on?
oh and what abt the deprecated string conversions ( see qn 2 ) ?
The file had already been closed once without a call to clear() after close(). Maybe that's the problem. Although it is odd that closing would fail instead of opening.


Like OP, I would also like to know about his second question. Only GCC complains when I pass string literals to functions asking for 'char *'s. If that method is "deprecated", what is the new method?
That's a good question. String literals are de-facto const char* since the characters referred to are typically embedded in a read-only page (ie, the code segment).

I would say the function's prototype needs to change to take an array of const chars.
I think I'd prefer to write a macro:
#define STRLITERAL(x) ((char*)(x))
It could be dangerous. Since the function is declared to take an array of chars, there is no API guarantee that the function will not try to modify the contents of the array which will bomb out (seg fault, gpf, etc).

Otherwise I would have suggested the same thing except I would have suggested using const_cast<char*>( "literal" ). [I use the C++ casts as a form of self-punishment to remind myself to write code that doesn't require casts in the first place.]
I know. However, if I'm knowingly passing a literal in the first place it's because it's a function I wrote and I know that particular parameter is not modified.
Why didn't I declare the parameter as const in the first place, you ask? Because const is one of the few things I hate about C++. I hate even more having to write for loops like this one, though:
for (std::map<wchar_t *,NONS_Variable *,wstrCmpCI>::iterator i=this->variables.begin();i!=this->variables.end();i++){/*...*/}
But unlike const, that is inevitable (no, I refuse to write yet another typedef purely on principle).
Last edited on
Sure, though it is a bit unsafe in that I can cast any type whatsoever to a char* without complaint from the compiler. (Just thinking in a large app with many developers, I know there will be people who try to abuse it).

I think const correctness is too important to ignore, though I agree with you that it is annoying to have to think about it and type it everywhere.

I almost invariably create typedefs for my containers when the scope of the container is greater than a local function. If I've coded everything generically enough, then I can completely change out the underlying container type with little or no code change elsewhere. This reduces a bit the length of the iterator type in the for loop.

Though you could use std::for_each() with a functor or with a lambda expression (from boost, though there is a steep learning curve for even the simplest of lambda expressions).

How can I make it such that in the following piece of code, file >> array[size].no occurs inside the loop and the loop exits when there are no more data to be read?

1
2
3
4
5
while ( file >> temp )
    {
        array[size].no = temp;
        size++;
    }

Last edited on
Make the while loop while file.good() perhaps (in case other errors occur).
so it becomes like this?
ok but how can I make it such that the file stream does not go into an error state and go about adjusting the while loop such that it works?

1
2
3
4
5
6
while ( file.good() )
    {
        file >> array[size].no;
        size++;
    }
I'm not sure you can without knowing a priori how many elements you are going to read from the file.

Your code above does not quite work: if file >> array[size].no fails, then you probably shouldn't increment size since you didn't actually read an element.
Topic archived. No new replies allowed.