Use gmx::File throughout CommandLineHelpWriter.
authorTeemu Murtola <teemu.murtola@gmail.com>
Fri, 4 May 2012 08:27:43 +0000 (11:27 +0300)
committerTeemu Murtola <teemu.murtola@gmail.com>
Tue, 8 May 2012 03:33:07 +0000 (06:33 +0300)
Added necessary methods to gmx::File to write basic text files, and use
those for writing the command-line help instead of accessing the
underlying file handle directly.

Change-Id: Iff680d0b5b193fc785f8deec85befdf040bec909

src/gromacs/commandline/cmdlinehelpwriter.cpp
src/gromacs/commandline/cmdlinehelpwriter.h
src/gromacs/commandline/tests/cmdlinehelpwriter.cpp
src/gromacs/trajectoryanalysis/cmdlinerunner.cpp
src/gromacs/utility/file.cpp
src/gromacs/utility/file.h

index eec9c6ede44a22fc16f725ccb94254dbb1683784..ba51a84cc2e7637eb1a1eed5237fb517bd755ed3 100644 (file)
@@ -37,9 +37,6 @@
  */
 #include "cmdlinehelpwriter.h"
 
-#include <cstdio>
-#include <cstdlib>
-
 #include <string>
 
 #include "gromacs/legacyheaders/smalloc.h"
@@ -52,6 +49,7 @@
 #include "gromacs/options/timeunitmanager.h"
 #include "gromacs/selection/selectionfileoptioninfo.h"
 #include "gromacs/selection/selectionoptioninfo.h"
+#include "gromacs/utility/file.h"
 #include "gromacs/utility/format.h"
 
 #include "cmdlinehelpwriter-impl.h"
@@ -91,13 +89,13 @@ class DescriptionWriter : public OptionsVisitor
 {
     public:
         //! Creates a helper object for writing section descriptions.
-        explicit DescriptionWriter(FILE *fp) : fp_(fp) {}
+        explicit DescriptionWriter(File *file) : file_(*file) {}
 
         virtual void visitSubSection(const Options &section);
         virtual void visitOption(const OptionInfo & /*option*/) { }
 
     private:
-        FILE                   *fp_;
+        File                   &file_;
 };
 
 void DescriptionWriter::visitSubSection(const Options &section)
@@ -107,12 +105,14 @@ void DescriptionWriter::visitSubSection(const Options &section)
         const std::string &title = section.title();
         if (!title.empty())
         {
-            fprintf(fp_, "%s\n\n", title.c_str());
+            file_.writeLine(title);
+            file_.writeLine();
         }
         TextLineWrapper wrapper;
         wrapper.setLineLength(78);
         std::string description(substituteMarkup(section.description()));
-        fprintf(fp_, "%s\n\n", wrapper.wrapToString(description).c_str());
+        file_.writeLine(wrapper.wrapToString(description));
+        file_.writeLine();
     }
     OptionsIterator(section).acceptSubSections(this);
 }
@@ -131,7 +131,7 @@ class FileParameterWriter : public OptionsTypeVisitor<FileNameOptionInfo>
 {
     public:
         //! Creates a helper object for writing file parameters.
-        explicit FileParameterWriter(FILE *fp);
+        explicit FileParameterWriter(File *file);
 
         //! Returns true if anything was written out.
         bool didOutput() const { return formatter_.didOutput(); }
@@ -140,12 +140,12 @@ class FileParameterWriter : public OptionsTypeVisitor<FileNameOptionInfo>
         virtual void visitOptionType(const FileNameOptionInfo &option);
 
     private:
-        FILE                   *fp_;
+        File                   &file_;
         TextTableFormatter      formatter_;
 };
 
-FileParameterWriter::FileParameterWriter(FILE *fp)
-    : fp_(fp)
+FileParameterWriter::FileParameterWriter(File *file)
+    : file_(*file)
 {
     formatter_.addColumn("Option",      6, false);
     formatter_.addColumn("Filename",    12, false);
@@ -244,8 +244,7 @@ void FileParameterWriter::visitOptionType(const FileNameOptionInfo &option)
     }
 
     // Do the formatting.
-    std::string row = formatter_.formatRow();
-    fprintf(fp_, "%s", row.c_str());
+    file_.writeString(formatter_.formatRow());
 }
 
 
@@ -262,7 +261,7 @@ class ParameterWriter : public OptionsVisitor
 {
     public:
         //! Creates a helper object for writing non-file parameters.
-        ParameterWriter(FILE *fp, const char *timeUnit);
+        ParameterWriter(File *file, const char *timeUnit);
 
         //! Sets the writer to show hidden options.
         void setShowHidden(bool bSet) { bShowHidden_ = bSet; }
@@ -273,14 +272,14 @@ class ParameterWriter : public OptionsVisitor
         virtual void visitOption(const OptionInfo &option);
 
     private:
-        FILE                   *fp_;
+        File                   &file_;
         TextTableFormatter      formatter_;
         const char             *timeUnit_;
         bool                    bShowHidden_;
 };
 
-ParameterWriter::ParameterWriter(FILE *fp, const char *timeUnit)
-    : fp_(fp), timeUnit_(timeUnit), bShowHidden_(false)
+ParameterWriter::ParameterWriter(File *file, const char *timeUnit)
+    : file_(*file), timeUnit_(timeUnit), bShowHidden_(false)
 {
     formatter_.addColumn("Option",      12, false);
     formatter_.addColumn("Type",         6, false);
@@ -338,8 +337,7 @@ void ParameterWriter::visitOption(const OptionInfo &option)
         formatter_.setColumnFirstLineOffset(3, 1);
     }
 
-    std::string row = formatter_.formatRow();
-    fprintf(fp_, "%s", row.c_str());
+    file_.writeString(formatter_.formatRow());
 }
 
 
@@ -356,7 +354,7 @@ class SelectionParameterWriter : public OptionsVisitor
 {
     public:
         //! Creates a helper object for writing selection parameters.
-        explicit SelectionParameterWriter(FILE *fp);
+        explicit SelectionParameterWriter(File *file);
 
         //! Returns true if anything was written out.
         bool didOutput() const { return formatter_.didOutput(); }
@@ -365,12 +363,12 @@ class SelectionParameterWriter : public OptionsVisitor
         virtual void visitOption(const OptionInfo &option);
 
     private:
-        FILE                   *fp_;
+        File                   &file_;
         TextTableFormatter      formatter_;
 };
 
-SelectionParameterWriter::SelectionParameterWriter(FILE *fp)
-    : fp_(fp)
+SelectionParameterWriter::SelectionParameterWriter(File *file)
+    : file_(*file)
 {
     formatter_.addColumn("Selection",   10, false);
     formatter_.addColumn("Description", 67, true);
@@ -395,8 +393,7 @@ void SelectionParameterWriter::visitOption(const OptionInfo &option)
     std::string name(formatString("-%s", option.name().c_str()));
     formatter_.addColumnLine(0, name);
     formatter_.addColumnLine(1, substituteMarkup(option.description()));
-    std::string row = formatter_.formatRow();
-    fprintf(fp_, "%s", row.c_str());
+    file_.writeString(formatter_.formatRow());
 
     // TODO: What to do with selection variables?
     // They are not printed as values for any option.
@@ -404,7 +401,7 @@ void SelectionParameterWriter::visitOption(const OptionInfo &option)
     {
         std::string value(option.formatValue(i));
         // TODO: Wrapping
-        fprintf(fp_, "    %s\n", value.c_str());
+        file_.writeLine(formatString("    %s", value.c_str()));
     }
 }
 
@@ -451,37 +448,38 @@ CommandLineHelpWriter &CommandLineHelpWriter::setTimeUnitString(const char *time
     return *this;
 }
 
-void CommandLineHelpWriter::writeHelp(FILE *fp)
+void CommandLineHelpWriter::writeHelp(File *file)
 {
     if (impl_->bShowDescriptions_)
     {
-        fprintf(fp, "DESCRIPTION\n"
-                    "-----------\n\n");
-        DescriptionWriter(fp).visitSubSection(impl_->options_);
+        file->writeLine("DESCRIPTION");
+        file->writeLine("-----------");
+        file->writeLine();
+        DescriptionWriter(file).visitSubSection(impl_->options_);
     }
     {
-        FileParameterWriter writer(fp);
+        FileParameterWriter writer(file);
         writer.visitSubSection(impl_->options_);
         if (writer.didOutput())
         {
-            fprintf(fp, "\n");
+            file->writeLine();
         }
     }
     {
-        ParameterWriter writer(fp, impl_->timeUnit_.c_str());
+        ParameterWriter writer(file, impl_->timeUnit_.c_str());
         writer.setShowHidden(impl_->bShowHidden_);
         writer.visitSubSection(impl_->options_);
         if (writer.didOutput())
         {
-            fprintf(fp, "\n");
+            file->writeLine();
         }
     }
     {
-        SelectionParameterWriter writer(fp);
+        SelectionParameterWriter writer(file);
         writer.visitSubSection(impl_->options_);
         if (writer.didOutput())
         {
-            fprintf(fp, "\n");
+            file->writeLine();
         }
     }
 }
index 6aff979a4cd280e3a0dced4bd71eaf1eb40c107a..7b01dc2b59fe4f23cca48f93a00fac7e92669bd1 100644 (file)
 #ifndef GMX_COMMANDLINE_CMDLINEHELPWRITER_H
 #define GMX_COMMANDLINE_CMDLINEHELPWRITER_H
 
-#include <cstdio>
-
 #include "../utility/common.h"
 
 namespace gmx
 {
 
+class File;
 class Options;
 
 /*! \brief
@@ -88,9 +87,9 @@ class CommandLineHelpWriter
         /*! \brief
          * Writes the help.
          *
-         * \param[in] fp  File to write the help to.
+         * \param[in] file  File to write the help to.
          */
-        void writeHelp(FILE *fp);
+        void writeHelp(File *file);
 
     private:
         class Impl;
index 8a73b0083bb33510f75fa8ba23ade3767f93cca6..0aceb2139601972b12699defc1eba233e2ff60a8 100644 (file)
@@ -88,7 +88,7 @@ CommandLineHelpWriterTest::~CommandLineHelpWriterTest()
 void CommandLineHelpWriterTest::checkHelp(gmx::CommandLineHelpWriter *writer)
 {
     gmx::File file(helpfile_, "w");
-    writer->writeHelp(file.handle());
+    writer->writeHelp(&file);
     file.close();
 
     checkFileContents(helpfile_, "HelpText");
index aa18c392e2e899fedb588cc75fbd79b26949f999..144da8e7b6fe128d3ffb6c1e82cf3097b75c1f19 100644 (file)
@@ -55,6 +55,7 @@
 #include "gromacs/trajectoryanalysis/cmdlinerunner.h"
 #include "gromacs/trajectoryanalysis/runnercommon.h"
 #include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/file.h"
 #include "gromacs/utility/gmxassert.h"
 
 namespace gmx
@@ -110,7 +111,7 @@ TrajectoryAnalysisCommandLineRunner::Impl::printHelp(
             .setShowDescriptions(flags & TrajectoryAnalysisRunnerCommon::efHelpShowDescriptions)
             .setShowHidden(flags & TrajectoryAnalysisRunnerCommon::efHelpShowHidden)
             .setTimeUnitString(settings.timeUnitManager().timeUnitAsString())
-            .writeHelp(stderr);
+            .writeHelp(&File::standardError());
     }
 }
 
index 1dcca51ca4531a1f36a776a1263c65964bbb0d23..3fe2fbb461e6058a5013bda0d2798af674da2435 100644 (file)
@@ -39,6 +39,7 @@
 
 #include <cerrno>
 #include <cstdio>
+#include <cstring>
 
 #include <algorithm>
 #include <string>
 namespace gmx
 {
 
-File::File(const char *filename, const char *mode)
-    : fp_(NULL)
+/*! \internal \brief
+ * Private implementation class for File.
+ *
+ * \ingroup module_utility
+ */
+class File::Impl
 {
-    open(filename, mode);
-}
+    public:
+        /*! \brief
+         * Initialize a file object with the given handle.
+         *
+         * \param[in]  fp     File handle to use (may be NULL).
+         * \param[in]  bClose Whether this object should close its file handle.
+         */
+        Impl(FILE *fp, bool bClose);
+        ~Impl();
 
-File::File(const std::string &filename, const char *mode)
-    : fp_(NULL)
+        //! File handle for this object (may be NULL).
+        FILE                   *fp_;
+        /*! \brief
+         * Whether \p fp_ should be closed by this object.
+         *
+         * Can be true if \p fp_ is NULL.
+         */
+        bool                    bClose_;
+};
+
+File::Impl::Impl(FILE *fp, bool bClose)
+    : fp_(fp), bClose_(bClose)
 {
-    open(filename, mode);
 }
 
-File::~File()
+File::Impl::~Impl()
 {
-    if (fp_ != NULL)
+    if (fp_ != NULL && bClose_)
     {
         if (fclose(fp_) != 0)
         {
@@ -74,13 +95,34 @@ File::~File()
     }
 }
 
+File::File(const char *filename, const char *mode)
+    : impl_(new Impl(NULL, true))
+{
+    open(filename, mode);
+}
+
+File::File(const std::string &filename, const char *mode)
+    : impl_(new Impl(NULL, true))
+{
+    open(filename, mode);
+}
+
+File::File(FILE *fp, bool bClose)
+    : impl_(new Impl(fp, bClose))
+{
+}
+
+File::~File()
+{
+}
+
 void File::open(const char *filename, const char *mode)
 {
-    GMX_RELEASE_ASSERT(fp_ == NULL,
+    GMX_RELEASE_ASSERT(impl_->fp_ == NULL,
                        "Attempted to open the same file object twice");
     // TODO: Port all necessary functionality from ffopen() here.
-    fp_ = fopen(filename, mode);
-    if (fp_ == NULL)
+    impl_->fp_ = fopen(filename, mode);
+    if (impl_->fp_ == NULL)
     {
         GMX_THROW_WITH_ERRNO(
                 FileIOError(formatString("Could not open file '%s'", filename)),
@@ -95,10 +137,12 @@ void File::open(const std::string &filename, const char *mode)
 
 void File::close()
 {
-    GMX_RELEASE_ASSERT(fp_ != NULL,
+    GMX_RELEASE_ASSERT(impl_->fp_ != NULL,
                        "Attempted to close a file object that is not open");
-    bool bOk = (fclose(fp_) == 0);
-    fp_ = NULL;
+    GMX_RELEASE_ASSERT(impl_->bClose_,
+                       "Attempted to close a file object that should not be");
+    bool bOk = (fclose(impl_->fp_) == 0);
+    impl_->fp_ = NULL;
     if (!bOk)
     {
         GMX_THROW_WITH_ERRNO(
@@ -108,21 +152,20 @@ void File::close()
 
 FILE *File::handle()
 {
-    GMX_RELEASE_ASSERT(fp_ != NULL,
+    GMX_RELEASE_ASSERT(impl_->fp_ != NULL,
                        "Attempted to access a file object that is not open");
-    return fp_;
+    return impl_->fp_;
 }
 
 void File::readBytes(void *buffer, size_t bytes)
 {
-    GMX_RELEASE_ASSERT(fp_ != NULL,
-                       "Attempted to access a file object that is not open");
     errno = 0;
+    FILE *fp = handle();
     // TODO: Retry based on errno or something else?
-    size_t bytesRead = std::fread(buffer, 1, bytes, fp_);
+    size_t bytesRead = std::fread(buffer, 1, bytes, fp);
     if (bytesRead != bytes)
     {
-        if (feof(fp_))
+        if (feof(fp))
         {
             GMX_THROW(FileIOError(
                         formatString("Premature end of file\n"
@@ -139,6 +182,38 @@ void File::readBytes(void *buffer, size_t bytes)
     }
 }
 
+void File::writeString(const char *str)
+{
+    if (fprintf(handle(), "%s", str) < 0)
+    {
+        GMX_THROW_WITH_ERRNO(FileIOError("Writing to file failed"),
+                             "fprintf", errno);
+    }
+}
+
+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
+File &File::standardError()
+{
+    static File stderrObject(stderr, false);
+    return stderrObject;
+}
+
 // static
 std::string File::readToString(const char *filename)
 {
index f158e44bc699c471760ee47ec588fba6d03bda6a..8f01a7445a83ef9344fdcae7e81080584755baa9 100644 (file)
@@ -65,6 +65,7 @@ class File
          *
          * \param[in] filename  Path of the file to open.
          * \param[in] mode      Mode to open the file in (for fopen()).
+         * \throws    std::bad_alloc if out of memory.
          * \throws    FileIOError on any I/O error.
          *
          * \see open(const char *, const char *)
@@ -120,6 +121,45 @@ class File
          */
         void readBytes(void *buffer, size_t bytes);
 
+        /*! \brief
+         * Writes a string to the file.
+         *
+         * \param[in]  str  String to write.
+         * \throws     FileIOError on any I/O error.
+         *
+         * The file must be open.
+         */
+        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
+         * Returns a File object for accessing stderr.
+         *
+         * \throws    std::bad_alloc if out of memory.
+         */
+        static File &standardError();
+
         /*! \brief
          * Reads contents of a file to a std::string.
          *
@@ -133,9 +173,20 @@ class File
         static std::string readToString(const std::string &filename);
 
     private:
-        FILE                   *fp_;
+        /*! \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 standardError().
+         */
+        File(FILE *fp, bool bClose);
+
+        class Impl;
 
-        GMX_DISALLOW_COPY_AND_ASSIGN(File);
+        PrivateImplPointer<Impl> impl_;
 };
 
 } // namespace gmx