diff --git a/include/rosa/support/csv/CSVWriter.hpp b/include/rosa/support/csv/CSVWriter.hpp index 201e41f..ee80281 100644 --- a/include/rosa/support/csv/CSVWriter.hpp +++ b/include/rosa/support/csv/CSVWriter.hpp @@ -1,257 +1,258 @@ //===-- rosa/support/csv/CSVWriter.hpp --------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file rosa/support/csv/CSVWriter.hpp /// /// \authors David Juhasz (david.juhasz@tuwien.ac.at) /// Edwin Willegger (edwin.willegger@tuwien.ac.at) /// /// \date 2017-2020 /// /// \brief Facitilities to write CSV files. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_CSV_CSVWRITER_HPP #define ROSA_SUPPORT_CSV_CSVWRITER_HPP #include #include #include #include #include #include "rosa/support/log.h" namespace rosa { namespace csv { /// Provides facilities to write values into a CSV file. /// /// The writer emits a comma, the character `,`, between each written values. /// The resulted stream is a flat CSV file as it consists of onlyone row, no new /// line is emitted. /// /// \tparam T type of values to write template class CSVWriter { public: /// Creates a new instance. /// /// \param [in,out] S output stream to write to /// \param Delimiter is the separator between the values of /// the csv file. /// /// \note The writer operates on non-binary outputs as long as \p S is in /// good state. CSVWriter(std::ostream &S, const char Delimiter = ',') : Str(S.good() && !(S.flags() & std::ios::binary) ? &S : nullptr), Delimiter(Delimiter), IsFirst(true) {} /// Tells if the last operation was successful. /// /// \return if the last operation was successful bool good(void) const noexcept { return Str != nullptr; } /// Writes an entry to the output stream. /// /// The implementation does anything only if the last operation was /// successful. If so, \p V is written to \c rosa::csv::CSVWriter::Str. /// The emitted value is preceded with a comma if the actual call is not the /// first one for \p this object. Success of the operation is checked at the /// end. /// /// \param V value to write void write(const T &V) { if (Str) { if (!IsFirst) { *Str << Delimiter; } else { IsFirst = false; } - *Str << V; + *Str << PRINTABLE(V); if (!Str->good()) { Str = nullptr; } } } /// Set the delimiter used in the csv file. /// \param Separator the character which separates the values in the csv file. inline void setDelimiter(char Separator) noexcept { this->Delimiter = Separator; } /// get the delimiter currently set to separate the values in the csv file. /// \return the current character, which is used to separte teh values in the /// csv file. inline char getDelimiter() const noexcept { return this->Delimiter; } private: std::ostream *Str; ///< Output stream to write to. char Delimiter; ///< The separator between the data entries. bool IsFirst; ///< Denotes if the next write would be the first one. }; /// Writes a tuple of values into a CSV file /// /// \tparam Ts types of values to write template class CSVTupleWriter { public: // typedef value_type ; ///< Type of values written. typedef std::tuple value_type; /// Creates a new instance. /// /// \param [in,out] S output stream to write to /// \param Delimiter is the separator between the values of /// the csv file. /// /// \note The writer operates on non-binary outputs as long as \p S is in /// good state. CSVTupleWriter(std::ostream &S, const char Delimiter = ',') : Str(S.good() && !(S.flags() & std::ios::binary) ? &S : nullptr), Delimiter(Delimiter), IsHeaderWritten(false), IsDataWritten(false) {} /// Tells if the last operation was successful. /// /// \return if the last operation was successful bool good(void) const noexcept { return Str != nullptr; } /// Write the values of a tuple to a CSV file with \c /// rosa::csv::CSVTupleWriter. /// /// \see rosa::csv::CSVTupleWriter /// /// /// \param [in,out] values tuple, which values are written in a recusive /// fashion into a stream. template void write(const std::tuple &values) { constexpr size_t size = sizeof...(Ts); + const auto value = PRINTABLE(std::get(values)); LOG_TRACE_STREAM << "Writing tuple values into file \n"; LOG_TRACE_STREAM << " Tuple has " << std::to_string(size) << " elements. \n"; - LOG_TRACE_STREAM << " Value is " << std::get(values); + LOG_TRACE_STREAM << " Value is " << value; if (Str) { /// Write the current element of the tuple into the stream and add a /// separtor after it, and call the function for the next element in the /// tuple. if constexpr (i + 1 != sizeof...(Ts)) { - *Str << std::get(values) << Delimiter; + *Str << value << Delimiter; write(values); /// If the last element is written into the stream than begin a new /// line. } else if constexpr (i + 1 == sizeof...(Ts)) { - *Str << std::get(values) << '\n'; + *Str << value << '\n'; /// every time the last data value of a line is written, the flag /// indicates that data was already written into the file. IsDataWritten = true; } } } /// Write the header values to a CSV file with \c rosa::csv::CSVTupleWriter. /// /// \note The function has no effect if anything has already been written /// to the output stream either by \c /// rosa::csv::CSVTupleWriter::writeHeader() or \c /// rosa::csv::CSVTupleWriter::write(). /// /// \see rosa::csv::CSVTupleWriter /// /// \param header the content of the header line. void writeHeader(const std::array &header) { size_t index = 0; /// write into the stream only, if it is not a nullptr, and if no data and /// no header was already written into it. if (Str && IsDataWritten == false && IsHeaderWritten == false) { index = 0; for (auto i = header.begin(); i != header.end(); ++i) { index = index + 1; /// write into the stream every entry with a delimiter, in this case ", /// " until the last entry if (index != header.size()) { *Str << *i << Delimiter; /// write the last entry into the stream, without any delimiter } else { *Str << *i; } } /// finish the header line and start a new line. *Str << '\n'; /// now it is not possible to write additional header lines. IsHeaderWritten = true; } } /// Set the delimiter used in the csv file. /// \param Separator the character which separates the values in the csv file. inline void setDelimiter(char Separator) noexcept { this->Delimiter = Separator; } /// get the delimiter currently set to separate the values in the csv file. /// \return the current character, which is used to separte teh values in the /// csv file. inline char getDelimiter() const noexcept { return this->Delimiter; } private: std::ostream *Str; ///< Output stream to write to. char Delimiter; ///< The separator between the data entries. bool IsHeaderWritten; ///< If an header line was already written into the ///< stream. If set than no additional header could be ///< written. bool IsDataWritten; ///< If one line of data has already been written into the ///< stream, than no headerline could be added. }; /// Writes all values of a tuple to a CSV file with \c /// rosa::csv::CSVTupleWriter. /// /// \see rosa::csv::CSVTupleWriter /// /// \tparam Ts types of values to write /// /// \param [in,out] W object to write with /// \param V values to write /// /// \return \p W after writing \p V with it template CSVTupleWriter &operator<<(CSVTupleWriter &W, const std::tuple &V) { W.write(V); return W; } /// Writes a value to a CSV file with \c rosa::csv::CSVWriter. /// /// \see rosa::csv::CSVWriter /// /// \tparam T type of value to write /// /// \param [in,out] W object to write with /// \param V value to write /// /// \return \p W after writing \p V with it template CSVWriter &operator<<(CSVWriter &W, const T &V) { W.write(V); return W; } } // End namespace csv } // End namespace rosa #endif // ROSA_SUPPORT_CSV_CSVWRITER_HPP