std.check.src.stream.hpp: src/stream.hpp
/*!
\file stream.hpp
\brief Header file which provides basic functions for reading text from files.
*/
#ifndef CHECK_STREAM_HPP
#define CHECK_STREAM_HPP
#include
#include "global.hpp"
#include "typeinfo.hpp"
#include "error.hpp"
#include "charset.hpp"
/*!
Optimal for the library feof() function enveloped as macros.
Native feof() is not used directly inside the library.
*/
#define CHECK_FEOF(STREAM) feof(STREAM)
/*!
Optimal for the library ferror() function enveloped as macros.
Native ferror() is not used directly inside the library.
*/
#define CHECK_FERROR(STREAM) ferror(STREAM)
/*!
Optimal for the library getc() function enveloped as macros.
Native getc() is not used directly inside the library.
*/
#define CHECK_GETC(STREAM) getc(STREAM)
/*!
Optimal for the library ungetc() function enveloped as macros.
Native ungetc() is not used directly inside the library.
*/
#define CHECK_UNGETC(SYMBOL, STREAM) ungetc(SYMBOL, STREAM)
/*
Locking is switched off by default, because it makes reading much slower and,
in fact, it is unnecessary when there are no concurrent threads.
*/
#if !defined(CHECK_LOCK_STREAM)
# if CHECK_MSC_VERSION(14, 0)
# undef CHECK_GETC
# undef CHECK_UNGETC
# define CHECK_GETC(STREAM) _getc_nolock(STREAM)
# define CHECK_UNGETC(SYMBOL, STREAM) _ungetc_nolock(SYMBOL, STREAM)
# elif CHECK_GCC_VERSION(4, 0) && !defined(__MINGW32__)
# undef CHECK_FEOF
# undef CHECK_FERROR
# undef CHECK_GETC
# define CHECK_FEOF(STREAM) feof_unlocked(STREAM)
# define CHECK_FERROR(STREAM) ferror_unlocked(STREAM)
# define CHECK_GETC(STREAM) getc_unlocked(STREAM)
# endif
#endif
// Disable annoying warning 4996: "'function' was declared deprecated"
#if CHECK_MSC_VERSION(14, 0)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
namespace Check
{
/*!
\ingroup input
\ingroup text
\relates Stream
\brief The %StreamMode enumeration specifies mode used to open a stream.
\see Stream::open()
*/
enum StreamMode
{
InputStream, //!< Open file as an test input file.
OutputStream, //!< Open file as an tested program output file.
AnswerStream //!< Open file as an master program output file.
};
/*!
\ingroup input
\ingroup text
\relates Stream
\brief The %BooleanFormat enumeration specifies how boolean values
should be read by Stream class.
\see Stream::operator >> (bool &) and Stream::boolFormat()
*/
enum BooleanFormat
{
/*! Read an \c int value, then compare it with zero; default mode. */
IntegerBoolean,
/*! Read a word, then compare it with two predefined strings. */
StringBoolean
};
/*!
\ingroup input
\ingroup text
\relates Stream
\brief The %WhitespaceMode enumeration specifies how whitespace
should be treated by Stream class.
\see Stream::whiteMode() and Stream::beforeWord()
*/
enum WhitespaceMode
{
SkipWhite, //!< Skip whitespace before most reading operations; default mode.
ConsiderWhite //!< Consider whitespace symbols as a part of the data.
};
/*!
\ingroup input
\ingroup text
\relates Stream
\brief The %EndOfFileMode enumeration specifies behaviour of end of file
detection by Stream class.
\see Stream::eofMode()
*/
enum EndOfFileMode
{
/*!
While data is expected treat end of file occurence as error and
raise EndOfFileError exception; default mode.
*/
FailAtEnd,
/*! Ignore end of file detection, return empty data if necessary. */
IgnoreEnd
};
/*!
\ingroup input
\ingroup text
\brief The %Stream class provides basic functions for reading information
of different types from a text file.
When it's possible this class tries to support functional interface similar to
that of the standard C++ istream class.
*/
class Stream
{
private:
enum { DefaultTrueFalseLength = 128 };
enum { StringReadBufferSize = 1024 };
static const Decision FormatErrorResult[3];
FILE *file;
std::string name;
StreamMode type;
bool closed;
// Indicates whether end of file occured while reading operation
bool endFound;
// Current format settings
BooleanFormat boolFmt;
std::string trueStr;
std::string falseStr;
WhitespaceMode spaceMode;
EndOfFileMode endFileMode;
// Characters to ignore before reading a word, could point to
// 'NoneChars' or 'SpaceChars' depending on whitespace mode
const CharSet *charsBeforeWord;
// Small storage for boolean or numeric values
std::string commonBuffer;
void initialize();
Stream &read(char &symbol);
Stream &read(
char *word, const size_t maxLength, const CharSet &before,
const CharSet &after, const bool putbackLast);
Stream &read(
std::string &word, const CharSet &before,
const CharSet &after, const bool putbackLast);
template
Stream &scan(Type &value);
public:
/*!
\brief The %SingleManip class provides storage class for information of
a single-argument stream manipulator.
Argument has type \a Argument and passed to the constructor and the
manipulator function as \a PassArgument.
\see DoubleManip and TripleManip
*/
template
class SingleManip
{
private:
friend class Stream;
typedef Stream &(*Function)(Stream &, PassArgument);
Function function;
Argument argument;
public:
//! Type of manipulator which argument is passed by reference.
typedef SingleManip PassByRef;
/*!
Constructs a manipulator object for function \a manip with
argument \a arg.
*/
SingleManip(Function manip, PassArgument arg)
: function(manip), argument(arg)
{ /* Do Nothing */ }
};
/*!
\brief The %DoubleManip class provides storage class for information of
a double-argument stream manipulator.
Arguments have types \a First and \a Second, and passed to the constructor
and the manipulator function as \a PassFirst and \a PassSecond,
respectively.
\see SingleManip and TripleManip
*/
template <
class First, class Second,
class PassFirst = First, class PassSecond = Second>
struct DoubleManip
{
private:
friend class Stream;
typedef Stream &(*Function)(Stream &, PassFirst, PassSecond);
Function function;
First first;
Second second;
public:
//! Type of manipulator which arguments are passed by references.
typedef DoubleManip PassByRef;
/*!
Constructs a manipulator object for function \a manip with
arguments \a firstArg and \a secondArg.
*/
DoubleManip(Function manip, PassFirst firstArg, PassSecond secondArg)
: function(manip), first(firstArg), second(secondArg)
{ /* Do Nothing */ }
};
/*!
\brief The %TripleManip class provides storage class for information of
a triple-argument stream manipulator
Arguments have types \a First, \a Second and \a Third, and passed to the
constructor and the manipulator function as \a PassFirst, \a PassSecond and
\a PassThird, respectively.
\see SingleManip and DoubleManip
*/
template <
class First, class Second, class Third,
class PassFirst = First, class PassSecond = Second, class PassThird = Third>
struct TripleManip
{
private:
friend class Stream;
typedef Stream &(*Function)(Stream &, PassFirst, PassSecond, PassThird);
Function function;
First first;
Second second;
Third third;
public:
//! Type of manipulator which arguments are passed by references.
typedef
TripleManip<
First, Second, Third, const First &, const Second &, const Third &>
PassByRef;
/*!
Constructs a manipulator object for function \a manip with arguments
\a firstArg, \a secondArg and \a thirdArg.
*/
TripleManip(
Function manip, PassFirst firstArg,
PassSecond secondArg, PassThird thirdArg)
: function(manip), first(firstArg), second(secondArg), third(thirdArg)
{ /* Do Nothing */ }
};
/*!
Constructs a stream. To start using it for reading it's necessary
to open a file.
\see open()
*/
Stream() { initialize(); }
/*!
Constructs a stream that operates on file \a fileName,
using \a openMode to define the open mode.
*/
Stream(const char *fileName, const StreamMode openMode)
{ initialize(); open(fileName, openMode); }
//! Destroys the stream.
~Stream() { close(); }
/*!
Returns the name of the file opened for reading by the stream.
If file is not opened, returns NULL pointer.
\see open(), isOpen(), and mode()
*/
const char *fileName() const { return closed ? NULL : name.c_str(); }
/*!
Returns true if file associated with the stream is open;
otherwise returns false.
\see open(), fileName(), and mode()
*/
bool isOpen() const { return !closed; }
/*!
Returns the file mode of the stream.
\see open(), isOpen(), and fileName()
*/
StreamMode mode() const { return type; }
/*!
Opens file \a fileName using \a openMode to define the open mode.
\throws InternalError
\see StreamMode
*/
void open(const char *fileName, const StreamMode openMode);
/*!
Closes the file. If file is not opened nothing happens.
\throws InternalError
*/
void close();
/*!
Reads and discards characters of \a charSet from the stream until a
character which does not belong to \a charSet is detected or
until atEnd() returns true.
This function is useful when reading a stream character by character.
\see opeartor >> (char &)
*/
Stream &skip(const CharSet &charSet = SpaceChars);
/*!
This is an overloaded member function, provided for convenience.
Reads and discards characters of \a charSet from the stream until a
character which does not belong to \a charSet is detected,
\a maxLength symbols are read, or until atEnd() returns true.
This function is useful when reading a stream character by character.
\see opeartor >> (char &)
*/
Stream &skip(const CharSet &charSet, const size_t maxLength);
/*!
Similar to skip(), but ignores characters until a character
which belongs to \a charSet is detected.
\see opeartor >> (char &)
*/
Stream &skipUntil(const CharSet &charSet = NoneChars)
{ return skip(~charSet); }
/*!
This is an overloaded member function, provided for convenience.
Similar to skip(), but ignores characters until a character
which belongs to \a charSet is detected.
\see opeartor >> (char &)
*/
Stream &skipUntil(const CharSet &charSet, const size_t maxLength)
{ return skip(~charSet, maxLength); }
/*!
Reads the next character from the stream without extracting it.
Returns EOF on end of file.
\see get() and putback()
*/
int peek();
/*!
Pushes \a symbol back to the stream. Pushed-back characters will be
returned in reverse order; only one pushback is guaranteed.
\throws InternalError
\see get() and peek()
*/
Stream &putback(const char symbol);
/*!
Reads the next character from the stream and returns it as an
\c unsigned \c char cast to an \a int, or EOF on end of file or error.
\see peek() and putback()
*/
int get() { const int c = CHECK_GETC(file); endFound = (c == EOF); return c; }
/*!
Reads the next character from the stream and stores it in \a symbol.
If end of file detected, an EndOfFileError exception is thrown.
\throws EndOfFileError
\see get(), peek(), and putback()
*/
Stream &get(char &symbol) { return read(symbol); }
/*!
Reads the next character from the stream ensuring it belongs to \a charSet
and stores it in \a symbol.
If end of file detected, an EndOfFileError exception is thrown.
If \a symbol does not belong to \a charSet, an UnhopedDataError exception is
thrown.
\throws EndOfFileError
\throws UnhopedDataError
\see get(), peek(), and putback()
*/
Stream &get(char &symbol, const CharSet &charSet);
/*!
Reads one word from the stream and stores it into the buffer pointed to
by \a word. At most one less than \a maxLength characters are read.
A terminating zero is stored after the last character in the buffer.
Word is a sequence of non-blank characters in a row ending with
a whitespace or end of file.
If end of file is detected before the first non-blank character,
an EndOfFileError exception is throwed. If current whitespace mode is
ConsiderWhite and the first character from the stream is blank,
empty word will be read.
\throws EndOfFileError
\see whiteMode() and operator >> (std::string &)
*/
Stream &get(char *word, const size_t maxLength)
{ return read(word, maxLength, *charsBeforeWord, SpaceChars, true); }
/*!
Reads one word from the stream and stores it into the \a word object.
Word is a sequence of non-blank characters in a row ending with
a whitespace or end of file.
If end of file is detected before the first non-blank character,
an EndOfFileError exception is throwed. If current whitespace mode is
ConsiderWhite and the first character from the stream is blank,
empty word will be read.
\throws EndOfFileError
\see whiteMode() and operator >> (std::string &)
*/
Stream &get(std::string &word)
{ return read(word, *charsBeforeWord, SpaceChars, true); }
/*!
Reads at most one less than \a maxLength characters from the stream and
stores them into the buffer pointed to by \a word. A terminating zero is
stored after the last character in the buffer.
If current whitespace mode is SkipWhite, before reading meaningful
characters this function ignores any blank symbols. Meaningful characters
are that belonging to \a charSet.
If end of file is detected before the first meaningful character,
an EndOfFileError exception is throwed.
\throws EndOfFileError
\see whiteMode() and operator >> (std::string &)
*/
Stream &get(char *word, const size_t maxLength, const CharSet &charSet)
{ return read(word, maxLength, *charsBeforeWord, ~charSet, true); }
/*!
Reads a sequence of \a charSet characters from the stream and stores them
into the \a word object.
If current whitespace mode is SkipWhite, before reading meaningful
characters this function ignores any blank symbols. Meaningful characters
are that belonging to \a charSet.
If end of file is detected before the first meaningful character,
an EndOfFileError exception is throwed.
\throws EndOfFileError
\see whiteMode() and operator >> (std::string &)
*/
Stream &get(std::string &word, const CharSet &charSet)
{ return read(word, *charsBeforeWord, ~charSet, true); }
/*!
Reads at most one less than \a maxLength characters from the stream and
stores them into the buffer pointed to by \a word. A terminating zero is
stored after the last character in the buffer.
Before reading meaningful characters this function ignores any symbols
from \a before. Reading stops after any of \a after characters are occurred
or end of file is detected.
If end of file is detected before the first meaningful character,
an EndOfFileError exception is throwed.
\throws EndOfFileError
\see operator >> (std::string &)
*/
Stream &get(
char *word, const size_t maxLength,
const CharSet &before, const CharSet &after)
{ return read(word, maxLength, before, after, true); }
/*!
Reads at most one less than \a maxLength characters from the stream and
stores them into the \a word object.
Before reading meaningful characters this function ignores any symbols
from \a before. Reading stops after any of \a after characters are occurred
or end of file is detected.
If end of file is detected before the first meaningful character,
an EndOfFileError exception is throwed.
\throws EndOfFileError
\see operator >> (std::string &)
*/
Stream &get(std::string &word, const CharSet &before, const CharSet &after)
{ return read(word, before, after, true); }
/*!
Reads at most one less than \a maxLength characters from the stream and
stores them into the buffer pointed to by \a word. A terminating zero is
stored after the last character in the buffer.
Before reading meaningful characters this function ignores any symbols
from \a before. Reading stops after an end of file detection or a newline.
If end of file is detected before the first meaningful character,
an EndOfFileError exception is throwed.
\throws EndOfFileError
\see operator >> (std::string &)
*/
Stream &getLine(
char *word, const size_t maxLength, const CharSet &before = NoneChars)
{ return read(word, maxLength, before, NewLineChars, false); }
/*!
Reads one line from the stream and stores it into the \a word object.
Before reading meaningful characters this function ignores any symbols
from \a before. Reading stops after an end of file detection or a newline.
If end of file is detected before the first meaningful character,
an EndOfFileError exception is throwed.
\throws EndOfFileError
\see operator >> (std::string &)
*/
Stream &getLine(std::string &word, const CharSet &before = NoneChars)
{ return read(word, before, NewLineChars, false); }
/*!
Applies manipulator without parameters to the stream.
Returns a reference to the stream, so several operators can be nested.
*/
Stream &operator >> (Stream &(*manip)(Stream &)) { return (*manip)(*this); }
/*!
Applies single-argument manipulator to the stream.
Returns a reference to the stream, so several operators can be nested.
*/
template
Stream &operator >> (const SingleManip &manip)
{ return (*manip.function)(*this, manip.argument); }
/*!
Applies double-argument manipulator to the stream.
Returns a reference to the stream, so several operators can be nested.
*/
template
Stream &operator >> (
const DoubleManip &manip)
{ return (*manip.function)(*this, manip.first, manip.second); }
/*!
Applies triple-argument manipulator to the stream.
Returns a reference to the stream, so several operators can be nested.
*/
template <
class First, class Second, class Third,
class PassFirst, class PassSecond, class PassThird>
Stream &operator >> (
const TripleManip<
First, Second, Third, PassFirst, PassSecond, PassThird> &manip)
{ return (*manip.function)(*this, manip.first, manip.second, manip.third); }
/*!
Reads a character from the stream and stores it in \a value.
Returns a reference to the stream, so several operators can be nested.
If current whitespace mode is SkipWhite, before reading a character blank
symbols are skipped. If end of file is detected before reading a character
an EndOfFileError exception are thrown.
\throws EndOfFileError
\see get(), peek() and whiteMode()
*/
Stream &operator >> (char &value)
{ skip(*charsBeforeWord); return read(value); }
/*!
This is an overloaded member function, provided for convenience.
Reads a boolean value from the stream and stores it in \a value.
Returns a reference to the stream, so several operators can be nested.
Depending on current boolean format value could be read as an integer or
as a string. When read as an integer, value is compared to zero to perform
conversion into \c bool type, when read as a string, value is compared to
predefined true and false strings.
\throws EndOfFileError
\throws FormatError
\throws WhitespaceError
\throws UnhopedDataError
\see BooleanFormat, boolFormat(), trueString(), and falseString()
*/
Stream &operator >> (bool &value);
/*!
This is an overloaded member function, provided for convenience.
Reads an integer from the stream and stores it in \a value, then returns a
reference to the stream. The number is casted to the correct type before it
is stored.
\throws EndOfFileError
\throws FormatError
\throws WhitespaceError
*/
Stream &operator >> (short int &value) { return scan(value); }
/*!
This is an overloaded member function, provided for convenience.
Stores the integer in the \c unsigned \c short \c int \a value.
\throws EndOfFileError
\throws FormatError
\throws WhitespaceError
*/
Stream &operator >> (unsigned short int &value) { return scan(value); }
/*!
This is an overloaded member function, provided for convenience.
Stores the integer in the \c int \a value.
\throws EndOfFileError
\throws FormatError
\throws WhitespaceError
*/
Stream &operator >> (int &value) { return scan(value); }
/*!
This is an overloaded member function, provided for convenience.
Stores the integer in the \c unsigned \c int \a value.
\throws EndOfFileError
\throws FormatError
\throws WhitespaceError
*/
Stream &operator >> (unsigned int &value) { return scan(value); }
/*!
This is an overloaded member function, provided for convenience.
Stores the integer in the \c long \c int \a value.
\throws EndOfFileError
\throws FormatError
\throws WhitespaceError
*/
Stream &operator >> (long int &value) { return scan(value); }
/*!
This is an overloaded member function, provided for convenience.
Stores the integer in the \c unsigned \c long \c int \a value.
\throws EndOfFileError
\throws FormatError
\throws WhitespaceError
*/
Stream &operator >> (unsigned long int &value) { return scan(value); }
#if defined(CHECK_SCAN64D)
/*!
This is an overloaded member function, provided for convenience.
Stores the integer in the \c long \c long \c int \a value.
\throws EndOfFileError
\throws FormatError
\throws WhitespaceError
*/
Stream &operator >> (long long int &value) { return scan(value); }
#endif
#if defined(CHECK_SCAN64U)
/*!
This is an overloaded member function, provided for convenience.
Stores the integer in the \c unsigned \c long \c long \c int \a value.
\throws EndOfFileError
\throws FormatError
\throws WhitespaceError
*/
Stream &operator >> (unsigned long long int &value) { return scan(value); }
#endif
/*!
This is an overloaded member function, provided for convenience.
Stores the integer in the \c float \a value.
\throws EndOfFileError
\throws FormatError
\throws WhitespaceError
*/
Stream &operator >> (float &value) { return scan(value); }
/*!
This is an overloaded member function, provided for convenience.
Stores the integer in the \c double \a value.
\throws EndOfFileError
\throws FormatError
\throws WhitespaceError
*/
Stream &operator >> (double &value) { return scan(value); }
/*!
This is an overloaded member function, provided for convenience.
Stores the integer in the \c long \c double \a value.
\throws EndOfFileError
\throws FormatError
\throws WhitespaceError
*/
Stream &operator >> (long double &value) { return scan(value); }
/*!
This is an overloaded member function, provided for convenience.
Reads a word from the stream and stores it in \a value, then returns a
reference to the stream. Words are separated by whitespace.
\throws EndOfFileError
\throws FormatError
\throws WhitespaceError
\see get(std::string &) and whiteMode()
*/
Stream &operator >> (std::string &value) { return get(value); }
/*!
Returns \e this pointer if end of file was not detected during last input
operation; otherwise returns \e NULL.
\see operator ! () and atEnd()
*/
operator const void *() const
{ return static_cast(endFound ? NULL : this); }
/*!
Returns true if end of file was detected during last input operation;
otherwise returns false.
\see operator const void *() and atEnd()
*/
bool operator ! () const { return endFound; }
/*!
Returns true if there is no more data to be read from the current line
of the stream; otherwise returns false.
\see seekEndOfLine() and atEnd()
*/
bool atEndOfLine() { return closed || isEndOfLine(peek()); }
/*!
Ingores all whitespace characters, then returns true if there is no more
data to read from the current line of the stream; otherwise returns false.
\see atEndOfLine() and atEnd()
*/
bool seekEndOfLine()
{ return closed || isEndOfLine(skip(BlankChars).peek()); }
/*!
Returns true if there is no more data to read from the stream;
otherwise returns false.
\see seekEnd() and atEndOfLine()
*/
bool atEnd() { return closed || CHECK_FEOF(file); }
/*!
Ingores all whitespace characters, then returns true if there is no more
data to be read from the stream; otherwise returns false.
\see atEnd() and atEndOfLine()
*/
bool seekEnd() { return closed || skip(SpaceChars).atEnd(); }
/*!
Returns the testing outcome associated with format error in the stream.
\see Decision
*/
Decision formatError() const { return FormatErrorResult[type]; }
/*!
Returns current boolean format.
\see BooleanFormat and setBoolFormat()
*/
BooleanFormat boolFormat() const { return boolFmt; }
/*!
Sets current boolean format to \a value. Default value is IntegerBoolean.
\see BooleanFormat and boolFormat()
*/
void setBoolFormat(const BooleanFormat value) { boolFmt = value; }
/*!
Returns current true string.
\see setTrueString(), boolFormat(), and falseString()
*/
const std::string &trueString() const { return trueStr; }
/*!
Sets current true string to \a value. By default true string is empty.
\see trueString()
*/
void setTrueString(const char *value) { trueStr = value; }
/*!
This is an overloaded member function, provided for convenience.
Sets current true string to \a value. By default true string is empty.
\see trueString()
*/
void setTrueString(const std::string &value) { trueStr = value; }
/*!
Returns current false string.
\see setFalseString(), boolFormat(), and trueString()
*/
const std::string &falseString() const { return falseStr; }
/*!
Sets current false string to \a value. By default false string is empty.
\see falseString()
*/
void setFalseString(const char *value) { falseStr = value; }
/*!
This is an overloaded member function, provided for convenience.
Sets current false string to \a value. By default false string is empty.
\see falseString()
*/
void setFalseString(const std::string &value) { falseStr = value; }
/*!
Returns current character set used to skip symbols before reading a word.
This character set is changed depending on current whitespace mode.
\see whiteMode()
*/
const CharSet &beforeWord() const { return *charsBeforeWord; }
/*!
Returns current whitespace mode.
\see WhitespaceMode and setWhiteMode()
*/
WhitespaceMode whiteMode() const { return spaceMode; }
/*!
Sets current whitespace mode to \a value. Default value is SkipWhite.
\see WhitespaceMode and whiteMode()
*/
void setWhiteMode(const WhitespaceMode value);
/*!
Returns current end of file mode.
\see EndOfFileMode and setEndMode()
*/
EndOfFileMode endMode() const { return endFileMode; }
/*!
Sets current end of file mode to \a value. Default value is FailAtEnd.
\see EndOfFileMode and endMode()
*/
void setEndMode(const EndOfFileMode value) { endFileMode = value; }
};
template
Stream &Stream::scan(Type &value)
{
// Without locking this way is approximately as effective as '%n' specifier
if (spaceMode == ConsiderWhite && isSpace(peek()))
incite(FormatErrorResult[type]);
if (fscanf(file, TypeInfo::scanFormat(), &value) != 1)
{
endFound = (!CHECK_FERROR(file) && CHECK_FEOF(file));
if (!endFound)
raise(
FormatErrorResult[type], "Value of type \'%s\' cannot be read",
TypeInfo::name());
else if (endFileMode == FailAtEnd)
incite(FormatErrorResult[type]);
}
return *this;
}
}
#if CHECK_MSC_VERSION(14, 0)
# pragma warning(pop)
#endif
#endif /* CHECK_STREAM_HPP */