Replace direct uses of gmx::File with streams
authorTeemu Murtola <teemu.murtola@gmail.com>
Fri, 26 Jun 2015 03:52:03 +0000 (06:52 +0300)
committerTeemu Murtola <teemu.murtola@gmail.com>
Sat, 4 Jul 2015 10:32:45 +0000 (12:32 +0200)
Replace all cases that used gmx::File with a stream-based
implementation.  Only static methods in gmx::File are called from
outside file.cpp (will be removed separately).

This is only direct replacement to remove uses of gmx::File; some
additional refactoring is necessary to support alternative streams for
unit testing.

Change-Id: I5e5905f9f8956909f3396359b18e9660f100bcaa

12 files changed:
src/gromacs/commandline/cmdlinehelpmodule.cpp
src/gromacs/selection/parsetree.cpp
src/gromacs/selection/selectioncollection.cpp
src/gromacs/trajectoryanalysis/cmdlinerunner.cpp
src/gromacs/utility.h
src/gromacs/utility/file.cpp
src/gromacs/utility/file.h
src/gromacs/utility/filestream.cpp
src/gromacs/utility/filestream.h
src/gromacs/utility/textreader.cpp [new file with mode: 0644]
src/gromacs/utility/textreader.h [new file with mode: 0644]
src/gromacs/utility/textstream.h

index 1261c142595e20c463ee38b5ae3599de9e977c00..0ebad1ecfcf51e147bf67de079dbe101e52ac162 100644 (file)
 #include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/baseversion.h"
 #include "gromacs/utility/exceptions.h"
-#include "gromacs/utility/file.h"
 #include "gromacs/utility/fileredirector.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/programcontext.h"
 #include "gromacs/utility/stringutil.h"
+#include "gromacs/utility/textreader.h"
 #include "gromacs/utility/textwriter.h"
 
 #include "shellcompletions.h"
@@ -522,9 +522,9 @@ HelpExportReStructuredText::HelpExportReStructuredText(
       binaryName_(helpModule.binaryName_),
       links_(eHelpOutputFormat_Rst)
 {
-    File             linksFile("links.dat", "r");
-    std::string      line;
-    while (linksFile.readLine(&line))
+    TextReader   linksFile("links.dat");
+    std::string  line;
+    while (linksFile.readLineTrimmed(&line))
     {
         links_.addLink("[REF]." + line + "[ref]",
                        formatString(":ref:`.%s <%s>`", line.c_str(), line.c_str()),
index d3d9096f18211cda5374801599cbd1adb69b096d..79162acd922aeeef53b394c5a21a6fd3363eaaea 100644 (file)
 #include "gromacs/selection/selection.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/exceptions.h"
-#include "gromacs/utility/file.h"
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/stringutil.h"
 
index 1e0726dbbd86f345290787e0a5287c0eabc46bf1..aba20c77174cc6c50f82f1c699634bca79431f06 100644 (file)
@@ -61,7 +61,6 @@
 #include "gromacs/selection/selhelp.h"
 #include "gromacs/topology/topology.h"
 #include "gromacs/utility/exceptions.h"
-#include "gromacs/utility/file.h"
 #include "gromacs/utility/filestream.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/smalloc.h"
@@ -130,20 +129,20 @@ namespace
 /*! \brief
  * Reads a single selection line from stdin.
  *
- * \param[in]  infile        File to read from (typically File::standardInput()).
+ * \param[in]  infile        Stream to read from (typically the StandardInputStream).
  * \param[in]  bInteractive  Whether to print interactive prompts.
  * \param[out] line          The read line in stored here.
  * \returns true if something was read, false if at end of input.
  *
  * Handles line continuation, reading also the continuing line(s) in one call.
  */
-bool promptLine(File *infile, bool bInteractive, std::string *line)
+bool promptLine(TextInputStream *infile, bool bInteractive, std::string *line)
 {
     if (bInteractive)
     {
         fprintf(stderr, "> ");
     }
-    if (!infile->readLineWithTrailingSpace(line))
+    if (!infile->readLine(line))
     {
         return false;
     }
@@ -157,7 +156,7 @@ bool promptLine(File *infile, bool bInteractive, std::string *line)
         std::string buffer;
         // Return value ignored, buffer remains empty and works correctly
         // if there is nothing to read.
-        infile->readLineWithTrailingSpace(&buffer);
+        infile->readLine(&buffer);
         line->append(buffer);
     }
     if (endsWith(*line, "\n"))
@@ -344,8 +343,8 @@ SelectionList runParser(yyscan_t scanner, bool bStdIn, int maxnr,
                 _gmx_sel_yypstate_new(), &_gmx_sel_yypstate_delete);
         if (bStdIn)
         {
-            File       &stdinFile(File::standardInput());
-            const bool  bInteractive = _gmx_sel_is_lexer_interactive(scanner);
+            TextInputStream &stdinFile(StandardInputStream::instance());
+            const bool       bInteractive = _gmx_sel_is_lexer_interactive(scanner);
             if (bInteractive)
             {
                 printCurrentStatus(sc, grps, oldCount, maxnr, context, true);
@@ -694,8 +693,8 @@ SelectionCollection::parseFromFile(const std::string &filename)
 
     try
     {
-        yyscan_t scanner;
-        File     file(filename, "r");
+        yyscan_t      scanner;
+        TextInputFile file(filename);
         // TODO: Exception-safe way of using the lexer.
         _gmx_sel_init_lexer(&scanner, &impl_->sc_, false, -1,
                             impl_->bExternalGroupsSet_,
index a6e3107cfbc1020c50b29c827496fbce68e099fb..675082f9b0acd6e57f83705c9902750868bb2d84 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013,2014, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014,2015, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -58,7 +58,7 @@
 #include "gromacs/trajectoryanalysis/analysismodule.h"
 #include "gromacs/trajectoryanalysis/analysissettings.h"
 #include "gromacs/utility/exceptions.h"
-#include "gromacs/utility/file.h"
+#include "gromacs/utility/filestream.h"
 #include "gromacs/utility/gmxassert.h"
 
 #include "runnercommon.h"
@@ -139,7 +139,7 @@ TrajectoryAnalysisCommandLineRunner::Impl::parseOptions(
 
     common->initIndexGroups(selections, bUseDefaultGroups_);
 
-    const bool bInteractive = File::standardInput().isInteractive();
+    const bool bInteractive = StandardInputStream::instance().isInteractive();
     seloptManager.parseRequestedFromStdin(bInteractive);
     common->doneIndexGroups(selections);
 
index b06d2d9104061a0bac85da4aad49f0aa73060388..7dc5f007f4822fb7f8b11b5ff36d950cbc9808cd 100644 (file)
  * implementations for these interfaces that just use the file system.
  *
  * The header textwriter.h provides gmx::TextWriter for more formatting support
- * when writing to a text stream.
+ * when writing to a text stream.  Similarly, textreader.h provides more
+ * formatting support when reading from a text stream.
  *
  * The header path.h declares helpers for manipulating paths as strings and for
  * managing directories and files.
index fcca1650b2194cf1465b05edae7f8cd02dc8d019..b5415a1c9c7dd62993dcf4cc1ae2570667d210e3 100644 (file)
 
 #include <sys/stat.h>
 
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/stringutil.h"
@@ -140,11 +136,6 @@ File::File(const std::string &filename, const char *mode)
     open(filename, mode);
 }
 
-File::File(FILE *fp, bool bClose)
-    : impl_(new Impl(fp, bClose))
-{
-}
-
 File::~File()
 {
 }
@@ -177,17 +168,6 @@ void File::close()
     }
 }
 
-bool File::isInteractive() const
-{
-    GMX_RELEASE_ASSERT(impl_->fp_ != NULL,
-                       "Attempted to access a file object that is not open");
-#ifdef HAVE_UNISTD_H
-    return isatty(fileno(impl_->fp_));
-#else
-    return true;
-#endif
-}
-
 FILE *File::handle()
 {
     GMX_RELEASE_ASSERT(impl_->fp_ != NULL,
@@ -220,46 +200,6 @@ void File::readBytes(void *buffer, size_t bytes)
     }
 }
 
-bool File::readLine(std::string *line)
-{
-    if (!readLineWithTrailingSpace(line))
-    {
-        return false;
-    }
-    size_t endPos = line->find_last_not_of(" \t\r\n");
-    if (endPos != std::string::npos)
-    {
-        line->resize(endPos + 1);
-    }
-    return true;
-}
-
-bool File::readLineWithTrailingSpace(std::string *line)
-{
-    line->clear();
-    const size_t bufsize = 256;
-    std::string  result;
-    char         buf[bufsize];
-    buf[0] = '\0';
-    FILE        *fp = handle();
-    while (fgets(buf, bufsize, fp) != NULL)
-    {
-        size_t length = std::strlen(buf);
-        result.append(buf, length);
-        if (length < bufsize - 1 || buf[length - 1] == '\n')
-        {
-            break;
-        }
-    }
-    if (ferror(fp))
-    {
-        GMX_THROW_WITH_ERRNO(FileIOError("Error while reading file"),
-                             "fgets", errno);
-    }
-    *line = result;
-    return !result.empty() || !feof(fp);
-}
-
 void File::writeString(const char *str)
 {
     if (fprintf(handle(), "%s", str) < 0)
@@ -269,22 +209,6 @@ void File::writeString(const char *str)
     }
 }
 
-void File::writeLine(const char *line)
-{
-    size_t length = std::strlen(line);
-
-    writeString(line);
-    if (length == 0 || line[length-1] != '\n')
-    {
-        writeString("\n");
-    }
-}
-
-void File::writeLine()
-{
-    writeString("\n");
-}
-
 // static
 bool File::exists(const char *filename)
 {
@@ -320,13 +244,6 @@ bool File::exists(const std::string &filename)
     return exists(filename.c_str());
 }
 
-// static
-File &File::standardInput()
-{
-    static File stdinObject(stdin, false);
-    return stdinObject;
-}
-
 // static
 std::string File::readToString(const char *filename)
 {
index 3be92f3b7c73281f0d9f2ad5093aef9fef6cb16e..9a6b9565d1b3f54e04b981668fef6ba670adaa1f 100644 (file)
@@ -119,17 +119,6 @@ class File
          */
         void close();
 
-        /*! \brief
-         * Returns whether the file is an interactive terminal.
-         *
-         * Only works on Unix, otherwise always returns true.
-         * It only makes sense to call this for File::standardInput() and
-         * friends.
-         *
-         * Thie file must be open.
-         * Does not throw.
-         */
-        bool isInteractive() const;
         /*! \brief
          * Returns a file handle for interfacing with C functions.
          *
@@ -148,41 +137,6 @@ class File
          * The file must be open.
          */
         void readBytes(void *buffer, size_t bytes);
-        /*! \brief
-         * Reads a single line from the file.
-         *
-         * \param[out] line    String to receive the line.
-         * \returns    false if nothing was read because the file ended.
-         * \throws     std::bad_alloc if out of memory.
-         * \throws     FileIOError on any I/O error.
-         *
-         * On error or when false is returned, \p line will be empty.
-         * Trailing space will be removed from the line.
-         * To loop over all lines in the file, use:
-         * \code
-           std::string line;
-           while (file.readLine(&line))
-           {
-               // ...
-           }
-           \endcode
-         */
-        bool readLine(std::string *line);
-        /*! \brief
-         * Reads a single line from the file.
-         *
-         * \param[out] line    String to receive the line.
-         * \returns    false if nothing was read because the file ended.
-         * \throws     std::bad_alloc if out of memory.
-         * \throws     FileIOError on any I/O error.
-         *
-         * On error or when false is returned, \p line will be empty.
-         * Works as readLine(), except that terminating newline will be present
-         * in \p line if it was present in the file.
-         *
-         * \see readLine()
-         */
-        bool readLineWithTrailingSpace(std::string *line);
 
         /*! \brief
          * Writes a string to the file.
@@ -195,26 +149,6 @@ class File
         void writeString(const char *str);
         //! \copydoc writeString(const char *)
         void writeString(const std::string &str) { writeString(str.c_str()); }
-        /*! \brief
-         * Writes a line to the file.
-         *
-         * \param[in]  line  Line to write.
-         * \throws     FileIOError on any I/O error.
-         *
-         * If \p line does not end in a newline, one newline is appended.
-         * Otherwise, works as writeString().
-         *
-         * The file must be open.
-         */
-        void writeLine(const char *line);
-        //! \copydoc writeLine(const char *)
-        void writeLine(const std::string &line) { writeLine(line.c_str()); }
-        /*! \brief
-         * Writes a newline to the file.
-         *
-         * \throws     FileIOError on any I/O error.
-         */
-        void writeLine();
 
         /*! \brief
          * Checks whether a file exists and is a regular file.
@@ -228,13 +162,6 @@ class File
         //! \copydoc exists(const char *)
         static bool exists(const std::string &filename);
 
-        /*! \brief
-         * Returns a File object for accessing stdin.
-         *
-         * \throws    std::bad_alloc if out of memory (only on first call).
-         */
-        static File &standardInput();
-
         /*! \brief
          * Reads contents of a file to a std::string.
          *
@@ -259,17 +186,6 @@ class File
                                         const std::string &text);
 
     private:
-        /*! \brief
-         * Initialize file object from an existing file handle.
-         *
-         * \param[in]  fp     %File handle to use (may be NULL).
-         * \param[in]  bClose Whether this object should close its file handle.
-         * \throws     std::bad_alloc if out of memory.
-         *
-         * Used internally to implement standardInput().
-         */
-        File(FILE *fp, bool bClose);
-
         class Impl;
 
         PrivateImplPointer<Impl> impl_;
index 08d975421fef26a33e760aa7b0a6ade3d719ae6d..7a2cf25dc716398ca5ad1e6d2eaf7eda6a49b09d 100644 (file)
 
 #include "filestream.h"
 
+#include "config.h"
+
 #include <cerrno>
 #include <cstdio>
 
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/stringutil.h"
 namespace gmx
 {
 
+namespace
+{
+
+//! Helper function for implementing readLine() for input streams.
+bool readLineImpl(FILE *fp, std::string *line)
+{
+    line->clear();
+    const size_t bufsize = 256;
+    std::string  result;
+    char         buf[bufsize];
+    buf[0] = '\0';
+    while (std::fgets(buf, bufsize, fp) != NULL)
+    {
+        const size_t length = std::strlen(buf);
+        result.append(buf, length);
+        if (length < bufsize - 1 || buf[length - 1] == '\n')
+        {
+            break;
+        }
+    }
+    if (std::ferror(fp))
+    {
+        GMX_THROW_WITH_ERRNO(FileIOError("Error while reading file"),
+                             "fgets", errno);
+    }
+    *line = result;
+    return !result.empty() || !std::feof(fp);
+}
+
+}   // namespace
+
 namespace internal
 {
 
@@ -122,6 +159,64 @@ class FileStreamImpl
 
 using internal::FileStreamImpl;
 
+/********************************************************************
+ * StandardInputStream
+ */
+
+bool StandardInputStream::isInteractive() const
+{
+#ifdef HAVE_UNISTD_H
+    return isatty(fileno(stdin));
+#else
+    return true;
+#endif
+}
+
+bool StandardInputStream::readLine(std::string *line)
+{
+    return readLineImpl(stdin, line);
+}
+
+// static
+StandardInputStream &StandardInputStream::instance()
+{
+    static StandardInputStream stdinObject;
+    return stdinObject;
+}
+
+/********************************************************************
+ * TextInputFile
+ */
+
+TextInputFile::TextInputFile(const std::string &filename)
+    : impl_(new FileStreamImpl(filename.c_str(), "r"))
+{
+}
+
+TextInputFile::TextInputFile(FILE *fp)
+    : impl_(new FileStreamImpl(fp))
+{
+}
+
+TextInputFile::~TextInputFile()
+{
+}
+
+FILE *TextInputFile::handle()
+{
+    return impl_->handle();
+}
+
+bool TextInputFile::readLine(std::string *line)
+{
+    return readLineImpl(impl_->handle(), line);
+}
+
+void TextInputFile::close()
+{
+    impl_->close();
+}
+
 /********************************************************************
  * TextOutputFile
  */
index 8a2d2a379f71c8df19cdcff788ce13d33caddf2b..f1e49339ca21e7e0900ea65bc0422069045c0089 100644 (file)
@@ -59,15 +59,48 @@ class FileStreamImpl;
 }
 
 /*! \libinternal \brief
- * Text output stream implementation for writing to a file.
+ * Text input stream implementation for reading from `stdin`.
  *
- * Implementations for the TextOutputStream methods throw FileIOError on any
+ * Implementations for the TextInputStream methods throw FileIOError on any
  * I/O error.
  *
  * \inlibraryapi
  * \ingroup module_utility
  */
-class TextOutputFile : public TextOutputStream
+class StandardInputStream : public TextInputStream
+{
+    public:
+        /*! \brief
+         * Returns whether `stdin` is an interactive terminal.
+         *
+         * Only works on Unix, otherwise always returns true.
+         *
+         * Does not throw.
+         */
+        bool isInteractive() const;
+
+        // From TextInputStream
+        virtual bool readLine(std::string *line);
+        virtual void close() {}
+
+        /*! \brief
+         * Returns a stream for accessing `stdin`.
+         *
+         * Does not throw.
+         */
+        static StandardInputStream &instance();
+};
+
+/*! \libinternal \brief
+ * Text input stream implementation for reading from a file.
+ *
+ * Implementations for the TextInputStream methods throw FileIOError on any
+ * I/O error.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class TextInputFile : public TextInputStream
 {
     public:
         /*! \brief
@@ -77,7 +110,7 @@ class TextOutputFile : public TextOutputStream
          * \throws     std::bad_alloc if out of memory.
          * \throws     FileIOError on any I/O error.
          */
-        explicit TextOutputFile(const std::string &filename);
+        explicit TextInputFile(const std::string &filename);
         /*! \brief
          * Initializes file object from an existing file handle.
          *
@@ -87,6 +120,39 @@ class TextOutputFile : public TextOutputStream
          * The caller is responsible of closing the file; close() does nothing
          * for an object constructed this way.
          */
+        explicit TextInputFile(FILE *fp);
+        virtual ~TextInputFile();
+
+        /*! \brief
+         * Returns a raw handle to the input file.
+         *
+         * This is provided for interoperability with older C-like code.
+         */
+        FILE *handle();
+
+        // From TextInputStream
+        virtual bool readLine(std::string *line);
+        virtual void close();
+
+    private:
+        PrivateImplPointer<internal::FileStreamImpl> impl_;
+};
+
+/*! \libinternal \brief
+ * Text output stream implementation for writing to a file.
+ *
+ * Implementations for the TextOutputStream methods throw FileIOError on any
+ * I/O error.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class TextOutputFile : public TextOutputStream
+{
+    public:
+        //! \copydoc TextInputFile::TextInputFile(const std::string &)
+        explicit TextOutputFile(const std::string &filename);
+        //! \copydoc TextInputFile::TextInputFile(FILE *)
         explicit TextOutputFile(FILE *fp);
         virtual ~TextOutputFile();
 
diff --git a/src/gromacs/utility/textreader.cpp b/src/gromacs/utility/textreader.cpp
new file mode 100644 (file)
index 0000000..4eef63e
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::TextReader.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_utility
+ */
+#include "gmxpre.h"
+
+#include "textreader.h"
+
+#include "gromacs/utility/filestream.h"
+#include "gromacs/utility/nodelete.h"
+#include "gromacs/utility/textstream.h"
+
+namespace gmx
+{
+
+class TextReader::Impl
+{
+    public:
+        explicit Impl(const TextInputStreamPointer &stream)
+            : stream_(stream)
+        {
+        }
+
+        TextInputStreamPointer stream_;
+};
+
+TextReader::TextReader(const std::string &filename)
+    : impl_(new Impl(TextInputStreamPointer(new TextInputFile(filename))))
+{
+}
+
+TextReader::TextReader(TextInputStream *stream)
+    : impl_(new Impl(TextInputStreamPointer(stream, no_delete<TextInputStream>())))
+{
+}
+
+TextReader::TextReader(const TextInputStreamPointer &stream)
+    : impl_(new Impl(stream))
+{
+}
+
+TextReader::~TextReader()
+{
+}
+
+bool TextReader::readLine(std::string *line)
+{
+    return impl_->stream_->readLine(line);
+}
+
+bool TextReader::readLineTrimmed(std::string *line)
+{
+    if (!readLine(line))
+    {
+        return false;
+    }
+    const size_t endPos = line->find_last_not_of(" \t\r\n");
+    if (endPos != std::string::npos)
+    {
+        line->resize(endPos + 1);
+    }
+    return true;
+}
+
+void TextReader::close()
+{
+    impl_->stream_->close();
+}
+
+} // namespace gmx
diff --git a/src/gromacs/utility/textreader.h b/src/gromacs/utility/textreader.h
new file mode 100644 (file)
index 0000000..0310f1a
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::TextReader.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_TEXTREADER_H
+#define GMX_UTILITY_TEXTREADER_H
+
+#include <string>
+
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/textstream.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Reads text from a TextInputStream.
+ *
+ * This class provides more formatted reading capabilities than reading raw
+ * lines from the stream (and a natural place to implement more such
+ * capabilities).
+ *
+ * All methods that read from the stream can throw any exceptions that the
+ * underlying stream throws.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class TextReader
+{
+    public:
+        /*! \brief
+         * Creates a reader that reads from specified file.
+         *
+         * \param[in]  filename  Path to the file to open.
+         * \throws     std::bad_alloc if out of memory.
+         * \throws     FileIOError on any I/O error.
+         *
+         * This constructor is provided for convenience for reading directly
+         * from a file, without the need to construct multiple objects.
+         */
+        explicit TextReader(const std::string &filename);
+        /*! \brief
+         * Creates a reader that reads from specified stream.
+         *
+         * \param[in]  stream  Stream to read from.
+         * \throws     std::bad_alloc if out of memory.
+         *
+         * The caller is responsible of the lifetime of the stream (should
+         * remain in existence as long as the reader exists).
+         *
+         * This constructor is provided for convenience for cases where the
+         * stream is not allocated with `new` and/or not managed by a
+         * boost::shared_ptr (e.g., if the stream is an object on the stack).
+         */
+        explicit TextReader(TextInputStream *stream);
+        /*! \brief
+         * Creates a reader that reads from specified stream.
+         *
+         * \param[in]  stream  Stream to read from.
+         * \throws     std::bad_alloc if out of memory.
+         *
+         * The reader keeps a reference to the stream, so the caller can pass
+         * in a temporary if necessary.
+         */
+        explicit TextReader(const TextInputStreamPointer &stream);
+        ~TextReader();
+
+        /*! \brief
+         * Reads a single line (including newline) from the stream.
+         *
+         * \param[out] line    String to receive the line.
+         * \returns    `false` if nothing was read because the file ended.
+         *
+         * On error or when false is returned, \p line will be empty.
+         * Newlines will be returned as part of \p line if it was present in
+         * the stream.
+         * To loop over all lines in the stream, use:
+         * \code
+           std::string line;
+           while (reader.readLine(&line))
+           {
+               // ...
+           }
+           \endcode
+         */
+        bool readLine(std::string *line);
+        /*! \brief
+         * Reads a single line from the stream.
+         *
+         * \param[out] line    String to receive the line.
+         * \returns    false if nothing was read because the file ended.
+         *
+         * On error or when false is returned, \p line will be empty.
+         * Works as readLine(), except that trailing whitespace will be removed
+         * from \p line.
+         *
+         * \see readLine()
+         */
+        bool readLineTrimmed(std::string *line);
+
+        /*! \brief
+         * Closes the underlying stream.
+         */
+        void close();
+
+    private:
+        class Impl;
+
+        PrivateImplPointer<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
index be4bc69fc0f598ef0d0c3fc5b20d5625d783aa4e..14fbfd410b5209ab6614b1e71c4ac96f93300e44 100644 (file)
 namespace gmx
 {
 
+/*! \libinternal \brief
+ * Interface for reading text.
+ *
+ * Concrete implementations can read the text from, e.g., a file or an in-memory
+ * string.  The main use is to allow unit tests to inject in-memory buffers
+ * instead of writing files to be read by the code under test, but there are
+ * also use cases outside the tests where it is useful to abstract out whether
+ * the input is from a real file or something else.
+ *
+ * To use more advanced formatting than reading raw lines, use TextReader.
+ *
+ * Both methods in the interface can throw std::bad_alloc or other exceptions
+ * that indicate failures to read from the stream.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class TextInputStream
+{
+    public:
+        virtual ~TextInputStream() {}
+
+        /*! \brief
+         * Reads a line (with newline included) from the stream.
+         *
+         * \param[out] line    String to receive the line.
+         * \returns    `false` if nothing was read because the stream ended.
+         *
+         * On error or when `false` is returned, \p line will be empty.
+         */
+        virtual bool readLine(std::string *line) = 0;
+        /*! \brief
+         * Closes the stream.
+         *
+         * It is not allowed to read from a stream after it has been closed.
+         * See TextOutputStream::close() for rationale for a close() method
+         * separate from the destructor.  For input, failures during close
+         * should be rare, but it is clearer to keep the interface symmetric.
+         */
+        virtual void close() = 0;
+};
+
 /*! \libinternal \brief
  * Interface for writing text.
  *
@@ -96,6 +138,8 @@ class TextOutputStream
         virtual void close() = 0;
 };
 
+//! Shorthand for a smart pointer to a TextInputStream.
+typedef boost::shared_ptr<TextInputStream> TextInputStreamPointer;
 //! Shorthand for a smart pointer to a TextOutputStream.
 typedef boost::shared_ptr<TextOutputStream> TextOutputStreamPointer;