Reputation: 381
This is a question as to the philosophy (canonical design) of user-written C++ input stream extraction operators (>>).
Assume that on entry to the >> operator implementation (for a user-written class), the eof flag is already set for the input stream.
Should a user-written extraction operator (>>)
If the second approach is used, it implies that the caller must always check the eof flag before any attempt is made to invoke the >> operator. The reason is that the >> operator might successfully extract an instance of the desired class and set the eof flag.
The original code follows. Based on the comments below, this code appears to be wrong. If eof is already set on input, the extraction operator will simply return with eof still set. It appears that if eof is set, but bad and fail are not set, then an extraction of a string should be done to set the fail bit. Of course, the fail bit can be set directly.
/* Implement the C/C++ >> (stream input) operator as a non-member
function */
std::istream &operator>>(std::istream& is, DecNumber &val) {
DecContext context{DecContext::defInit};
uint32_t status;
/* The true value below prevents whitespace from being skipped */
std::istream::sentry s(is, true);
std::string inStr;
/* Check if the input stream is in a good state. Just return to the
caller if the input stremm is not in a good state. The caller
must handle this condition. */
if(!s)
return is;
/* Get a string from the input stream. This string is converted to
a DecNumber below. Just return to the caller if this step causes
any stream related errors. Note that reaching the end of the
input is not a stream related error here. A decimal number might
be the absolute last thing in the stream. */
is >> inStr;
if (is.bad() || is.fail())
return is;
/* Try to convert the string to a DecNumber using the default context
value */
decNumberFromString(val.getDecVal(), inStr.c_str(), context.getDecCont());
status = context.DecContextGetStatus();
/* Remove a few status bits we don't care about */
status &= ~(DEC_Inexact + DEC_Rounded);
if (status)
is.setstate(std::ios_base::failbit);
return is;
}
Upvotes: 2
Views: 2624
Reputation: 1
"If the second approach is used, it implies that the caller must always check the eof flag before any attempt is made to invoke the >> operator."
No, why do you think they need to do so?
"Should a user-written extraction operator (>>) set the failure flag (because no instance of the desired object can be found) or should it just return to the caller with the eof flag still set."
The latter option of course, you're not supposed to manage stream states in overloaded extraction operators, unless you add your own validation rules (e.g. for expecting specific character patterns with the std::string
field). It will usually be done correctly with the sub extraction operations that the overloaded operator uses.
Supposed you have something like follows:
struct MyType {
std::string field1;
int field2;
double field3;
}
std::istream& operator>>(std::istream& is, MyType& myinstance) {
is >> field1;
is >> field2;
is >> field3;
return is;
}
Each of the extractions will set the fields to their default constructed values, in case the operator>>()
fails, because the stream is in eof()
state, and the value will be left in it's original state for the field that was attempted to extract.
I actually don't see a need to have any additional check for eof()
or setting the stream to fail()
state in your overloaded input operator.
The client (caller) will simply use something like e.g.
std::ifstream input("MyFile.txt");
std::vector<MyType> allObjects;
MyType curObject;
while(input >> curObject) {
allObjects.push_back(curObject);
}
You see, no need to check for input.eof()
anywhere.
Upvotes: 1
Reputation: 10064
You should implement solution 1.
When in doubt, just look at what's already being done. As you can see below, the fail bit is being set if we try to read from a stream in EOF state.
Note that EOF is not the only way to fail though. Try setting std::string vals = "52 43 A";
in the code below.
failbit
should be set if for any reason, operator>>
doesn't actually stream a value. EOF is just one of those reasons.
#include <sstream>
#include <iostream>
#include <string>
void print_stream (std::istream & print_me, int const & i)
{
std::cout << "i: " << i << "\n";
std::ios_base::iostate bits = print_me.rdstate();
std::cout << "good: " << (bits & std::ios_base::goodbit) <<
", bad: " << (bits & std::ios_base::badbit) <<
", fail: " << (bits & std::ios_base::failbit) <<
", eof: " << (bits & std::ios_base::eofbit) << "\n";
std::cout << "\n----------------------------\n\n";
}
int main (void)
{
std::string vals = "52 43";
std::istringstream iss(vals);
int i;
iss >> i;
print_stream (iss, i);
iss >> i;
print_stream (iss, i);
iss >> i;
print_stream (iss, i);
iss >> i;
print_stream (iss, i);
return 0;
}
Outputs
$ ./a.exe
i: 52
good: 0, bad: 0, fail: 0, eof: 0
----------------------------
i: 43
good: 0, bad: 0, fail: 0, eof: 2
----------------------------
i: 43
good: 0, bad: 0, fail: 4, eof: 2
----------------------------
i: 43
good: 0, bad: 0, fail: 4, eof: 2
----------------------------
Note that the typical read pattern loop is some variation of...
while (input >> var >> var2 >> var3)
{
// failbit is not set. All reads succeeded.
// Do Stuff
}
If you need to detect whether the fail happened at some point during reading of multiple values then yea, you need to be a little more sophisticated and do some testing like...
while (true)
{
if (input >> var)
{
// We successfully read first value
if (input >> var2 >> var3)
{
// We succesfully read all the values!
// Do stuff
}
else
{
ErrorLog ("Partial line read!");
break;
}
else
{
// Nothing else to read
break;
}
}
Upvotes: 3