diff --git a/include/rosa/support/writer/namespace.h b/include/rosa/support/writer/namespace.h new file mode 100644 index 0000000..3349a93 --- /dev/null +++ b/include/rosa/support/writer/namespace.h @@ -0,0 +1,25 @@ +//===-- rosa/support/writer/namespace.h --------------------------*- C++ -*-===/ +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===/ +/// +/// \file rosa/support/writer/namespace.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Documentation for the namespace \c rosa::writer. +/// +//===----------------------------------------------------------------------===/ + +#ifndef ROSA_SUPPORT_WRITER_NAMESPACE_H +#define ROSA_SUPPORT_WRITER_NAMESPACE_H + +namespace rosa { +/// Provides facilities to work with writers. +namespace writer {} +} // End namespace rosa + +#endif // ROSA_SUPPORT_WRITER_NAMESPACE_H diff --git a/include/rosa/support/writer/split_tuple_writer.hpp b/include/rosa/support/writer/split_tuple_writer.hpp new file mode 100644 index 0000000..22e3a35 --- /dev/null +++ b/include/rosa/support/writer/split_tuple_writer.hpp @@ -0,0 +1,554 @@ +//===-- rosa/support/writer/split_tuple_writer.hpp ---------------*- C++ -*-===/ +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===/ +/// +/// \file rosa/support/writer/split_tuple_writer.hpp +/// +/// \authors David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Facilities to split writers of \c std::tuple into writers of +/// their elements. +/// +//===----------------------------------------------------------------------===/ + +#ifndef ROSA_SUPPORT_WRITER_SPLIT_TUPLE_WRITER_HPP +#define ROSA_SUPPORT_WRITER_SPLIT_TUPLE_WRITER_HPP + +#include "rosa/support/debug.hpp" +#include "rosa/support/sequence.hpp" + +#include +#include +#include + +namespace rosa { +namespace writer { + +/// What to do when splitted element writers got destructed so that some +/// incomplete tuples remain in the buffer. +enum IncompleteTuplePolicy { + Ignore, ///< ignore incomplete tuples, data discarded + Flush ///< flush incomplete tuples, use default value for missing elements +}; + +/// Anonymous namespace providing implementation for splitting tuple writers; +/// consider it private. +namespace { + +/// Writes values into a buffer based on an index. +/// +/// The values are first buffered and flushed out later once a corresponding +/// tuple gets complete (i.e., other elementwise writers put the corresponding +/// values into the buffer). Since the underlying buffer waits for tuples to +/// become complete, the elementwise writers are expected to provide the same +/// number of values for the buffer (i.e., completing all tuples that have an +/// element). That is, however, not enforced in any ways. +/// +/// Once all elementwise writers are destructed, the underlying buffer gets +/// destructed as well. Should the buffer contain some values, which are +/// elements of incomplete tuples, the destructor of the buffer handles the +/// situation according to the \c rosa::writer::IncompleteTuplePolicy set when +/// the elementwise writers were created by \c rosa::writer::splitTupleWriter(). +/// +/// \tparam TB buffer to write into +/// \tparam I index of the values to write to \p TB +/// +/// \note \p TB is expected to implemnet an interface matching that of \c +/// TupleWriterBuffer. +/// +/// \note the +template +class SplittedElementWriter { +public: + /// Type alias for the values the writer writes. + using T = typename TB::template element_type; + + /// \defgroup SplittedElemenWriterTypedefs Typedefs of + /// \c SplittedElementWriter + /// + /// Useful `typedef`s for writers. + /// + ///@{ + typedef T value_type; ///< Type of values written. + typedef T &reference; ///< Reference to the type written + ///@} + + /// Creates a new instance. + /// + /// \param Buffer buffer \p this object writes values into + /// + /// \note \p Buffer is captured as a \c std::shared_ptr because it is shared + /// among writers for its element positions. + SplittedElementWriter(std::shared_ptr Buffer) : Buffer(Buffer) {} + + /// Tells how many values are still in the buffer. + /// + /// Values are buffered as long as all elements of their corresponding tuples + /// are written into the buffer by other elementwise writers and in turn + /// complete tuples written by the buffer itself. This function tells how many + /// values are still in the buffer waiting for their containing tuples to be + /// complete and written out from the buffer. + /// + /// \note The function simply calles the corresponding function of \p TB. + /// + /// \return how many values are still in the buffer. + size_t buffered(void) const noexcept { + return Buffer->template buffered(); + } + + /// Tells if the last write operation of the buffer was successful. + /// + /// Values are buffered until their corresponding tuples gets complete. The + /// buffer writes complete tuples by the underlying writer. This function + /// tells if the buffer successfully has written the latest complete tuple by + /// the underlying writer. + /// + /// Once this function returns \c false, further \c write() calls has no + /// effect and the last \c buffered() values will not be written to the + /// underlying writer of \c std::tuple. + /// + /// \note The function simply calles the corresponding function of \p TB. + /// + /// \return if the last write operation was successful + bool good(void) const noexcept { return Buffer->good(); } + + /// Writes an entry to the buffer. + /// + /// The function has no effect if an earlier write operation of the buffer has + /// failed, that is \c good() returns \c false. + /// + /// \note The function simply calles the corresponding function of \p TB. + /// + /// \param V value to write + void write(const T &V) { Buffer->template write(V); } + +private: + std::shared_ptr Buffer; ///< Buffer \p this object writes into +}; + +/// Writes an element of a tuple. +/// +/// \see SplittedElementWriter +/// +/// \tparam TB type of the buffer \p W writes into +/// \tparam I index of the values \p W write to \p TB +/// +/// \param [in,out] W object to write with +/// \param V value to write +/// +/// \return \p W after writing \p V with it +template +SplittedElementWriter & +operator<<(SplittedElementWriter &W, + const typename SplittedElementWriter::value_type &V) { + W.write(V); + return W; +} + +///\defgroup TupleBufferContainer Type converter turning a \c std::tuple of +///types into a \c std::tuple of container of those types. +/// +/// The new type is used for buffering elements of tuples separately. +/// +///@{ + +/// Template declaration. +/// +/// \tparam T type to convert +/// +/// \note The template is defined only when \p T is a \c std::tuple. +/// +/// Usage for a type \c Tuple as:\code +/// typename TupleBufferContainer::Type +/// \endcode +template struct TupleBufferContainer; + +/// Template definition for \c std::tuple. +template struct TupleBufferContainer> { + /// The converted type. + using Type = std::tuple...>; +}; + +///@} + +/// Convenience template type alias for easy use of \c TupleBufferContainer. +/// +/// Converts a \c std::tuple of types into a \c std::tuple of container of those +/// types. +/// +/// \tparam Tuple type to convert +template +using tuple_buffer_container_t = typename TupleBufferContainer::Type; + +/// Buffer for element values for writer of \c std::tuple. +/// +/// The class can be instantiated with a writer of \c std::tuple and provides +/// an interface for writing elements into tuples one by one. +/// +/// \note The class is utilized by \c SplittedElementWriter. +/// +/// \tparam TupleWriter the writer type that handles values of \c std::tuple +/// +/// \note The elements of the written \c std::tuple need to be default +/// constructible because of \c rosa::writer::IncompleteTuplePolicy::Flush. +/// +/// \todo Consider thread safety of the class. +template +class TupleWriterBuffer { +public: + /// Type alias for the value type of \p TupleWriter. + /// \note The type is expected to be \c std::tuple. + using writer_value_type = typename TupleWriter::value_type; + + /// The number of elements of \c writer_value_type. + static constexpr size_t writer_value_size = + std::tuple_size_v; + + /// Template type alias to get element types of \c writer_value_type by + /// index. + /// \tparam I the index of the element + template + using element_type = + typename std::tuple_element::type; + + /// Type alias for index sequence for accessing elements of \c + /// writer_value_size. + using element_idx_seq_t = seq_t; + + /// Creates a new instance. + /// + /// \param Writer the writer \p this object uses to write tuples + /// \param Policy what to do with incomplete tuples when destructing \p + /// this object + TupleWriterBuffer(TupleWriter &&Writer, + const IncompleteTuplePolicy Policy) noexcept + : Writer(Writer), Policy(Policy), Buffer() {} + + /// Destructor. + /// + /// Should \p this object has some values in \c Buffer (i.e., incomplete + /// tuples), the destructor takes care of them according to \c Policy. + /// + /// \note The destructor may flush the buffered values if the last write + /// operation was successful, that is \c good() returns \c true. + ~TupleWriterBuffer(void) noexcept { + if (good()) { + switch (Policy) { + default: + ASSERT(false && "Unexpected Policy"); + case Ignore: + // Do not care about incomplete tuples in the buffer. + break; + case Flush: + // Whatever we have in the buffer, flush it out. + flush(); + break; + } + } + } + + /// Tells how many values are in the buffer for index \p I. + /// + /// \tparam I the index of the element to check + /// + /// \return the number of values in buffer for index \p I + template size_t buffered(void) const noexcept { + return std::get(Buffer).size(); + } + + /// Tells if the last write operation was successful. + /// + /// Values are buffered until their corresponding tuples gets complete. + /// Once a tuple becomes complete, it is written by \c Writer. This function + /// tells if writing the latest complete tuple was successful. + /// + /// Once this function returns \c false, further \c write() calls has no + /// effect and the last \c buffered() values will not be written to the + /// underlying writer of \c std::tuple. + /// + /// \return if the last write operation was successful + bool good(void) const noexcept { return Writer.good(); } + + /// Writes an entry to the buffer for index \p I. + /// + /// The function has no effect if an earlier write operation of the buffer has + /// failed, that is \c good() returns \c false. + /// + /// The value is buffered first and written by \p Writer once its + /// corresponding tuple becomes complete (i.e., all other elements of the + /// tuple are written into \p this object by corresponding elementwise + /// writers). + /// + /// \tparam I the index of the element to write + /// + /// \param V value to write + template void write(const element_type &V) { + ASSERT(!hasCompleteTuple(element_idx_seq_t())); + if (good()) { + std::get(Buffer).push(V); + if (hasCompleteTuple(element_idx_seq_t())) { + writeTuple(false); + } + } + ASSERT(!hasCompleteTuple(element_idx_seq_t())); + } + +private: + /// Tells whether \c Buffer is completely empty, all elements of it are empty. + /// + /// \tparam S0 indices for accessing elements of \c std::tuple + /// + /// \note The parameter provides indices as template parameter \p S0 and its + /// actual value is ignored. + /// + /// \return whether all elements of \c Buffer are empty + template bool empty(Seq) const noexcept { + return (true && ... && std::get(Buffer).empty()); + } + + /// Tells whether \c Buffer contains at least one complete tuple. + /// + /// \tparam S0 indices for accessing elements of \c std::tuple + /// + /// \note The parameter provides indices as template parameter \p S0 and its + /// actual value is ignored. + /// + /// \return whether there is at least one complete tuple in \c Buffer + template bool hasCompleteTuple(Seq) const noexcept { + return (true && ... && !std::get(Buffer).empty()); + } + + /// Makes sure \c Buffer has one complete tuple in front. + /// + /// If the tuple in front of \c Buffer is not complete, missing elements are + /// default constructed in \c Buffer to complete the tuple. + /// + /// \tparam S0 indices for accessing elements of \c std::tuple + /// + /// \note The parameter provides indices as template parameter \p S0 and its + /// actual value is ignored. + template void makeCompleteTuple(Seq) noexcept { + auto addDefaultIfEmpty = [](auto &Queue) { + if (Queue.empty()) { + Queue.emplace(); // default construct a value in the empty queue. + } + }; + (addDefaultIfEmpty(std::get(Buffer)), ...); + } + + /// Gives the first complete tuple from \c Buffer. + /// + /// \tparam S0 indices for accessing elements of \c std::tuple + /// + /// \note The parameter provides indices as template parameter \p S0 and its + /// actual value is ignored. + /// + /// \return the first complete tuple from \c Buffer + /// + /// \pre There is at least one complete tuple in \c Buffer:\code + /// hasCompleteTuple(element_idx_seq_t()) + /// \endcode + template writer_value_type front(Seq) const noexcept { + ASSERT(hasCompleteTuple(element_idx_seq_t())); + return {std::get(Buffer).front()...}; + } + + /// Removes the first complete tuple from \c Buffer. + /// + /// \tparam S0 indices for accessing elements of \c std::tuple + /// + /// \note The parameter provides indices as template parameter \p S0 and its + /// actual value is ignored. + /// + /// \pre There is at least one complete tuple in \c Buffer:\code + /// hasCompleteTuple(element_idx_seq_t()) + /// \endcode + template void pop(Seq) noexcept { + ASSERT(hasCompleteTuple(element_idx_seq_t())); + (std::get(Buffer).pop(), ...); + } + + /// Takes the first tuple from \c Buffer and writes it with \c Writer. + /// + /// \c Buffer need to contain a complete tuple to write it with \c Writer. The + /// function makes sure that the tuple in front of \c Buffer is complete if \p + /// MakeComplete is true. The tuple in fron of \c Buffer are expected to be + /// complete otherwise. + /// + /// \param MakeComplete whether to make the first tuple complete + /// + /// \pre There is at least one complete tuple in \c Buffer if not \p + /// MakeComplete: \code + /// MakeComplete || hasCompleteTuple(element_idx_seq_t()) + /// \endcode + void writeTuple(const bool MakeComplete) noexcept { + if (MakeComplete) { + makeCompleteTuple(element_idx_seq_t()); + } + ASSERT(hasCompleteTuple(element_idx_seq_t())); + Writer.write(front(element_idx_seq_t())); + pop(element_idx_seq_t()); + } + + /// Empties \c Buffer by writing out all tuples from it so that missing + /// elements of incomplete tuples are extended with default constructed + /// values. + void flush(void) noexcept { + while (!empty(element_idx_seq_t())) { + ASSERT(!hasCompleteTuple(element_idx_seq_t())); // Sanity check. + writeTuple(true); + } + } + + TupleWriter Writer; ///< The splitted writer + const IncompleteTuplePolicy Policy; ///< what to do with incomplete tuples + ///< when destructing \p this object + tuple_buffer_container_t + Buffer; ///< Container for elementwise buffering +}; + +/// Template type alias for writer of an element based on buffered writer of +/// \c std::tuple. +/// +/// The alias utilizes \c SplittedElementWriter for writing element values by +/// \p TB. +/// +/// \tparam TB buffer to write into +/// \tparam I index of the values to write to \p TB +template +using element_writer_t = SplittedElementWriter; + +///\defgroup ElementWriters Type converter turning a buffer of \c std::tuple +///into a \c std::tuple of corresponding \c element_writer_t. +/// +///@{ + +/// Template declaration. +/// +/// \tparam TB buffer to write into +/// \tparam S type providing indices for accessing elements of \c std::tuple +/// +/// \note \p TB is expected to implement an interface matching that of \c +/// TupleWriterBuffer. +/// +/// \note The template is defined only when \p S is a \c rosa::Seq. +/// +/// Usage for a proper buffer type \c TB:\code +/// typename ElementIteratorWriters::Type +/// \endcode +template struct ElementWriters; + +/// Template definition. +template +struct ElementWriters> { + /// The converted type. + using Type = std::tuple...>; +}; + +///@} + +/// Convenience template type alias for easy use of \c ElementIteratorWriters. +/// +/// Converts a buffer of \c std::tuple into a \c std::tuple of corresponding \c +/// element_writer_t. +/// +/// \tparam TB buffer to write into +/// +/// \note \p TB is expected to implement an interface matching that of \c +/// TupleWriterBuffer. +template +using element_writers_t = + typename ElementWriters::Type; + +/// Template type alias for turning a writer of \c std::tuple into +/// corresponding \c element_writers_t. +/// +/// The alias utilizes \c TupleWriterBuffer for buffering values before writing +/// them by \p TupleWriter. The elementwise writers are \c +/// SplittedElementWriter. +/// +/// \tparam TupleWriter writer of \c std::tuple +template +using splitted_tuple_writers_t = + element_writers_t>; + +/// Creates elementwise writers for a writer of \c std::tuple. +/// +/// \note Implementation for \c rosa::iterator::splitTupleWriter. +/// +/// \tparam TupleWriter writer of \c std::tuple +/// \tparam S0 indices for accessing elements of \c std::tuple +/// +/// \param Writer the writer to write tuples with +/// \param Policy how to handle incomplete tuples when destructing the +/// underlying buffer +/// +/// \note The last parameter provides indices as template parameter \p S0 and +/// its actual value is ignored. +/// +/// \return \c std::tuple of elementwise writers corresponding to \p Writer +template +splitted_tuple_writers_t +splitTupleWriterImpl(TupleWriter &&Writer, const IncompleteTuplePolicy Policy, + Seq) noexcept { + using TB = TupleWriterBuffer; + // Create a buffer in shared pointer. The buffer will be destructed once + // all corresponding element writers (created below) have been destructed. + auto Buffer = std::make_shared(std::move(Writer), Policy); + return {element_writer_t(Buffer)...}; +} + +} // End namespace + +/// Creates elementwise writers for a writer of \c std::tuple. +/// +/// \note The implementation utilizes \c splitTupleWriterImpl. +/// +/// Obtain elementwise writers for a writer \p Writer of type \c TupleWriter of +/// \c std::tuple as \code +/// auto [T1Writer, T2Writer, T3Writer] = splitTupleWriter(std::move(Writer), +/// Ignore); +/// \endcode +/// +/// The function returns a tuple of writers, which can be assigned to variables +/// using structured binding. +/// +/// The function moves its first argument (i.e., \p Writer cannot be used +/// directly after splitting it). If the first argument is a variable, it needs +/// to be moved explicitly by \c std::move() as in the example. +/// +/// While a tuple could be written with \p Writer directly as \code +/// Writer << std::make_tuple(T1(), T2(), T3()); +/// \endcode The same tuple is written with splitted writers as \code +/// T1Writer << T1(); +/// T2Writer << T2(); +/// T3Writer << T3(); +/// \endcode +/// +/// \tparam TupleWriter writer of \c std::tuple +/// +/// \note The elements of the written \c std::tuple need to be default +/// constructible because of \c rosa::writer::IncompleteTuplePolicy::Flush. +/// +/// \param Writer the writer to write tuples with +/// \param Policy what to do with incomplete tuples when destructing the +/// underlying buffer +/// +/// \return \c std::tuple of elementwise writers corresponding to \p Writer +template +splitted_tuple_writers_t +splitTupleWriter(TupleWriter &&Writer, + const IncompleteTuplePolicy Policy) noexcept { + return splitTupleWriterImpl( + std::move(Writer), Policy, + seq_t>()); +} + +} // End namespace writer +} // End namespace rosa + +#endif // ROSA_SUPPORT_WRITER_SPLIT_TUPLE_WRITER_HPP diff --git a/lib/support/CMakeLists.txt b/lib/support/CMakeLists.txt index 12a3334..318486d 100644 --- a/lib/support/CMakeLists.txt +++ b/lib/support/CMakeLists.txt @@ -1,42 +1,46 @@ set(LIB_INCLUDE_DIR ${ROSA_MAIN_INCLUDE_DIR}/rosa/support) add_library(ROSASupport ${LIB_INCLUDE_DIR}/debug.hpp debug.cpp ${LIB_INCLUDE_DIR}/terminal_colors.h terminal_colors.cpp ${LIB_INCLUDE_DIR}/log.h log.cpp ${LIB_INCLUDE_DIR}/math.hpp math.cpp ${LIB_INCLUDE_DIR}/type_helper.hpp type_helper.cpp ${LIB_INCLUDE_DIR}/types.hpp types.cpp ${LIB_INCLUDE_DIR}/atom.hpp atom.cpp ${LIB_INCLUDE_DIR}/type_pair.hpp type_pair.cpp ${LIB_INCLUDE_DIR}/type_list.hpp type_list.cpp ${LIB_INCLUDE_DIR}/squashed_int.hpp squashed_int.cpp ${LIB_INCLUDE_DIR}/type_numbers.hpp type_numbers.cpp ${LIB_INCLUDE_DIR}/type_token.hpp type_token.cpp ${LIB_INCLUDE_DIR}/tokenized_storages.hpp tokenized_storages.cpp ${LIB_INCLUDE_DIR}/sequence.hpp sequence.cpp ${LIB_INCLUDE_DIR}/csv/namespace.h csv/namespace.cpp ${LIB_INCLUDE_DIR}/csv/CSVReader.hpp csv/CSVReader.cpp ${LIB_INCLUDE_DIR}/csv/CSVWriter.hpp csv/CSVWriter.cpp ${LIB_INCLUDE_DIR}/iterator/namespace.h iterator/namespace.cpp ${LIB_INCLUDE_DIR}/iterator/split_tuple_iterator.hpp iterator/split_tuple_iterator.cpp + ${LIB_INCLUDE_DIR}/writer/namespace.h + writer/namespace.cpp + ${LIB_INCLUDE_DIR}/writer/split_tuple_writer.hpp + writer/split_tuple_writer.cpp ) diff --git a/lib/support/writer/namespace.cpp b/lib/support/writer/namespace.cpp new file mode 100644 index 0000000..b659e2d --- /dev/null +++ b/lib/support/writer/namespace.cpp @@ -0,0 +1,20 @@ +//===-- support/writer/namespace.cpp -----------------------------*- C++ -*-===/ +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===/ +/// +/// \file support/writer/namespace.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Placeholder for rosa/support/iterator/namespace.h. +/// +/// \note Empty implementation, source file here to have a compile database +/// entry for rosa/support/writer/namespace.h. +/// +//===----------------------------------------------------------------------===/ + +#include "rosa/support/writer/namespace.h" diff --git a/lib/support/writer/split_tuple_writer.cpp b/lib/support/writer/split_tuple_writer.cpp new file mode 100644 index 0000000..651e012 --- /dev/null +++ b/lib/support/writer/split_tuple_writer.cpp @@ -0,0 +1,20 @@ +//===-- support/writer/split_tuple_writer.cpp --------------------*- C++ -*-===/ +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===/ +/// +/// \file support/writer/split_tuple_writer.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Implementation for rosa/support/writer/split_tuple_writer.hpp. +/// +/// \note Empty implementation, source file here to have a compile database +/// entry for rosa/support/writer/split_tuple_writer.hpp. +/// +//===----------------------------------------------------------------------===/ + +#include "rosa/support/writer/split_tuple_writer.hpp"