diff --git a/examples/CSVFiles/main.cpp b/examples/CSVFiles/main.cpp index f57c01c..b2c43ce 100644 --- a/examples/CSVFiles/main.cpp +++ b/examples/CSVFiles/main.cpp @@ -1,350 +1,352 @@ //===-- examples/CSVFiles/main.cpp ------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file examples/basic-system/basic-system.cpp /// /// \author Edwin Willegger (edwin.willegger@tuwien.ac.at) /// /// \date 2019 /// /// \brief A simple example on the basic \c rosa::csv, \c rosa::iterator and /// \c rosa::writer classes. Focus is on the tuple impementations. /// //===----------------------------------------------------------------------===// #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //includes for an complete example to read and write //with sensors and agents. #include "rosa/deluxe/DeluxeContext.hpp" #include "rosa/config/version.h" //includes to test the basic functionality //to read and write tuples. #include "rosa/support/csv/CSVReader.hpp" #include "rosa/support/csv/CSVWriter.hpp" #include "rosa/support/iterator/split_tuple_iterator.hpp" #include "rosa/support/writer/split_tuple_writer.hpp" /// the name of the example const std::string ExampleName = "csvfiles"; /// How many cycles of simulation to perform. const size_t NumberOfSimulationCycles = 10; /// Paths for the CSV files for simulation. /// input csv files const std::string csvPath = "../examples/CSVFiles/"; const std::string csvFileWithHeader = csvPath + "HR-New.csv"; const std::string csvFileNoHeader = csvPath + "HR.csv"; const std::string csvFileHeaderSemi = csvPath + "HR-New-Semicolon.csv"; /// output csv files const std::string csvFileWriteHea = csvPath + "csvwriter_noheader.csv"; const std::string csvFileWriteNoHeaSplit = csvPath + "csvSplitwriter_noheader.csv"; using namespace rosa; /// /// This function tests the basic CSVIterator capablities, and shows you /// how you could work with this class. /// void testtupleCSVReader(void){ //different streams to get the csv data out of the files //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_2(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_3(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_4(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_5(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_6(csvFileWithHeader); //file contains no header an valid data entries, delimter = ',' std::ifstream file2(csvFileNoHeader); //file contains header and valid data entries, delimter = ';' std::ifstream file3(csvFileHeaderSemi); csv::CSVIterator it(file_header_data); it.setDelimeter(','); it++; it++; //if you iterate over the end of file, the last values //of the file will remain in the data structure but no //error occurs. it++; it++; //------------------------------------------------------------------- // a possiblity to get the data out of the iterator std::tuple value = *it; // // Show the value of one iterator // LOG_INFO( "Values are: "); LOG_INFO(std::get<0>(value) ); LOG_INFO(std::get<1>(value) ); //-------------------------------------------------------------------- //testing differnet parameters to the constructor //uncomment to see that it is not possible to iterate over an vector in the tuple. //rosa::csv::CSVIterator> it2(file, 1); //try to skip a valid number of lines after the header csv::CSVIterator it2_0(file_header_data_2, 1); //try to skip a valid number of lines after the header, but you assume that the file has no header //uncomment this line to crash the programm //csv::CSVIterator it2_1(file_header_data_3, 0, csv::HeaderInformation::HasNoHeader); //try to skip a valid number of lines after the header, but you assume that the file has no header //uncomment this line to crash the program //csv::CSVIterator it2_2(file_header_data_4, 1, csv::HeaderInformation::HasNoHeader); //try to skip a valid number of lines of a file without header csv::CSVIterator it2_3(file2, 1, csv::HeaderInformation::HasNoHeader); //try to skip a valid number of lines after the header, but with different delimeter csv::CSVIterator it2_4(file3, 2, csv::HeaderInformation::HasHeader, ';'); // if you skip more lines than valid, you generate an infinte loop //csv::CSVIterator it3(file_header_data_5, 500); //if you don't need data from all columns just select the number of columns you //need. You get the data back from the first column (index 0) to the fourth column //all values from the fifth column are ignored. csv::CSVIterator it4(file_header_data_6); } /// /// This function tests the basic CSVTupleWriter capablities, and shows you /// how you could work with this class. /// void testtupleCSVWriter(void){ // // Create output writer with an file // std::ofstream file_header_out(csvFileWriteHea); csv::CSVTupleWriter wri(file_header_out); // // Create test tuples // std::tuple values(5, 8.3, "hallo"); std::tuple values2(3, 8.3, "end"); // // Create test header lines for the test tuples // std::array header {"zero column", "first column", "second column"}; std::array headerWrong {"zero column", "first column", "second column", "third column"}; std::array headerWrongShort {"zero column", "first column"}; //if you uncomment this line than it would be possible for you to write the header into the stream //in the next line. //wri.write(values); wri.writeHeader(header); wri.write(values); wri.write(values); // it is not possible to write an additional header into the stream. wri.writeHeader(header); wri.write(values); wri << values; wri << values2; //uncomment this line to see, that you can't write a header with the too many elements. //wri.writeHeader(headerWrong); //uncomment this line to see, that you can't write a header with the too few elements. //wri.writeHeader(headerWrongShort); } /// /// This function tests the basic splitTupleIterator capablities, and shows you /// how you could work with this class, this class is used if you want to split /// a CSVIterator in separate parts. /// void testsplitTupleIterator(void) { // // Create deluxe context // std::unique_ptr C = deluxe::DeluxeContext::create(ExampleName); // // Create deluxe sensors. // LOG_INFO("Creating sensors."); // All sensors are created without defining a normal generator function, but // with the default value of the second argument. That, however, requires the // data type to be explicitly defined. This is good for simulation only. // Three different sensors were created, this is just a random number taken. AgentHandle Elem0Sensor = C->createSensor("Element1 Sensor"); AgentHandle Elem1Sensor = C->createSensor("Element2 Sensor"); AgentHandle Elem2Sensor = C->createSensor("Element3 Sensor"); // // Initialize deluxe context for simulation. // C->initializeSimulation(); // Type aliases for iterators using Iterator = rosa::csv::CSVIterator; using IteratorValue = std::tuple; static_assert (std::is_same::value, "Iterator must provide tuples" ); // // Open CSV file and register the columns to the corresponding sensors. // std::ifstream TestCSV(csvFileWithHeader); // // Test data looks like: // Element1, Element2, Element3, Element4, Element5 -- is the header line // 3, 5, 8, 9.5, 17 -- first line of values // 100, -8, 30, 18.8, 29 -- other line of values were also in the file // 5, 20, -100, -200.1, -30 -- if you have less number of values than simulation rounds all values // -- beyond your last value will be zero. //get element iterator ranges auto [Elem0Range, Elem1Range, Elem2Range] = iterator::splitTupleIterator(Iterator(TestCSV), Iterator()); - //dissect ranges into begin and end iterators by structred bindings + //dissect a range into begin and end iterators by structred bindings auto[Elem0Begin, Elem0End] = Elem0Range; - auto[Elem1Begin, Elem1End] = Elem1Range; - auto[Elem2Begin, Elem2End] = Elem2Range; + //deissect a range with functions + auto Elem1Begin = iterator::begin(Elem1Range); + auto Elem1End = iterator::end(Elem1Range); C->registerSensorValues(Elem0Sensor, std::move(Elem0Begin), Elem0End); C->registerSensorValues(Elem1Sensor, std::move(Elem1Begin), Elem1End); - C->registerSensorValues(Elem2Sensor, std::move(Elem2Begin), Elem2End); + C->registerSensorValues(Elem2Sensor, std::move(iterator::begin(Elem2Range)), + iterator::end(Elem2Range)); // // Simulate. // C->simulate(NumberOfSimulationCycles); } /// /// This function tests the basic splitTupleWriter capablities, and shows you /// how you could work with this class, this class is used if you want to split /// a CSVWriter in separate parts. /// void testsplitTupleWriter(void){ // // Create output writer with an file // std::ofstream file_header_out(csvFileWriteNoHeaSplit); csv::CSVTupleWriter wri(file_header_out); // if you omit, the type definition in the template, than auto generated types were used, // and they may not fit to the used CSVTupleWriter. wri << std::make_tuple(1000, 50.6, "tuple_created"); auto [T0Writer, T1Writer, T2Writer] = writer::splitTupleWriter(std::move(wri), writer::IncompleteTuplePolicy::Ignore); //writing elements in sequential order into the writer classes, but you can write the values into the writers in //a random order. T0Writer << (500); T1Writer << (3.0); T2Writer << "splitted writter"; T2Writer << "splitting is cool"; T0Writer << (-30); T1Writer << (-0.004); // you can also write more often values into a writer and later into the other writers // all data will be processed correctly into the right order. T0Writer << (1); T0Writer << (2); T1Writer << (-0.4); T0Writer << (3); T2Writer << "again"; T0Writer << (4); T1Writer << (-0.1); T1Writer << (-0.2); T2Writer << "and"; T1Writer << (-0.3); T2Writer << "splitting"; T2Writer << "once again"; // again writing data of one tuple entry to the different writers in a random fashion. T1Writer << (-0.004); T2Writer << "splitting is cool"; T0Writer << (-30); } int main(void){ LOG_INFO_STREAM << library_string() << " -- " << terminal::Color::Red << ExampleName << " example" << terminal::Color::Default << '\n'; // // Testing CSVWriter. // LOG_INFO("Testing CSVWriter CSVTupleItrator implementation: "); testtupleCSVWriter(); // // Testing CSVReader. // LOG_INFO("Testing CSVReader CSVTupleIterator implementation: "); testtupleCSVReader(); // // Testing SplitTupleIterator. // LOG_INFO("Testing SplitTupleIterator: "); testsplitTupleIterator(); // // Testing SplitTupleWriter. // LOG_INFO("Testing SplitTupleWriter: "); testsplitTupleWriter(); // // info that user knows programm has finished. // LOG_INFO( "All tests finished."); return 0; } diff --git a/include/rosa/support/iterator/split_tuple_iterator.hpp b/include/rosa/support/iterator/split_tuple_iterator.hpp index 3d1b9e9..f5af821 100644 --- a/include/rosa/support/iterator/split_tuple_iterator.hpp +++ b/include/rosa/support/iterator/split_tuple_iterator.hpp @@ -1,431 +1,469 @@ //===-- rosa/support/iterator/split_tuple_iterator.hpp ----------*- C++ -*-===/ // // The RoSA Framework // //===----------------------------------------------------------------------===/ /// /// \file rosa/support/iterator/split_tuple_iterator.hpp /// /// \authors David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2019 /// /// \brief Facilities to split iterators of \c std::tuple into iterators of /// their elements. /// //===----------------------------------------------------------------------===/ #ifndef ROSA_SUPPORT_ITERATOR_SPLIT_TUPLE_ITERATOR_HPP #define ROSA_SUPPORT_ITERATOR_SPLIT_TUPLE_ITERATOR_HPP #include "rosa/support/debug.hpp" #include "rosa/support/sequence.hpp" #include #include #include #include namespace rosa { namespace iterator { /// Anonymous namespace providing implementation for splitting tuple iterators; /// consider it private. namespace { /// Iterator of values provided by a buffer based on an index /// /// \tparam TB buffer providing values to iterate /// \tparam I index of the values to iterator from \p TB /// /// \note \p TB is expected to implemnet an interface matching that of \c /// TupleIteratorBuffer. template class SplittedElementIterator { public: /// Type alias for the values the iterator iterates. using T = typename TB::template element_type; /// \defgroup SplittedElementIteratorTypedefs Typedefs of /// \c SplittedElementIterator /// /// Standard `typedef`s for iterators. /// ///@{ typedef std::input_iterator_tag iterator_category; ///< Category of the iterator. typedef T value_type; ///< Type of values iterated over. typedef std::size_t difference_type; ///< Type to identify distance. typedef T *pointer; ///< Pointer to the type iterated over. typedef T &reference; ///< Reference to the type iterated ver. ///@} /// Creates a new instance. /// /// \param Buffer buffer providing values for \p this object /// /// \note \p Buffer is captured as a \c std::shared_ptr because it is shared /// among iterators for its element positions. SplittedElementIterator(std::shared_ptr Buffer) : Buffer(Buffer), Value() { // \c Value is initialized empty so the first incrementation here will read // the first value. ++(*this); } /// Creates an empty new instance. SplittedElementIterator(void) noexcept : Buffer(), Value() {} /// Pre-increment operator. /// /// The implementation reads the next value from \c Buffer. If no more values /// can be provided by \c Buffer, \p this object becomes empty and the /// operator has no further effect. /// /// \return \p this object after incrementing it. SplittedElementIterator &operator++() { if (Buffer->template hasMore()) { Value = Buffer->template next(); } else { // Reached end of range, remove reference of \c Buffer. Buffer.reset(); } return *this; } /// Post-increment operator. /// /// The implementation uses the pre-increment operator and returns a copy of /// the original state of \p this object. /// /// \return \p this object before incrementing it. SplittedElementIterator operator++(int) { SplittedElementIterator Tmp(*this); ++(*this); return Tmp; } /// Returns a constant reference to the current value. /// /// \note Should not dereference the iterator when it is empty. /// /// \return constant reference to the current value. const T &operator*(void)const noexcept { return Value; } /// Returns a constant pointer to the current value. /// /// \note Should not dereference the iterator when it is empty. /// /// \return constant pointer to the current value. const T *operator->(void)const noexcept { return &Value; } /// Tells if \p this object is equal to another one. /// /// Two \c SplittedElementIterator instances are equal if and only if they are /// the same or both are empty. /// /// \param RHS other object to compare to /// /// \return whether \p this object is equal with \p RHS bool operator==(const SplittedElementIterator &RHS) const noexcept { return ((this == &RHS) || (!Buffer && !RHS.Buffer)); } /// Tells if \p this object is not equal to another one. /// /// \see SplittedElementIterator::operator== /// /// \param RHS other object to compare to /// /// \return whether \p this object is not equal with \p RHS. bool operator!=(const SplittedElementIterator &RHS) const noexcept { return !((*this) == RHS); } private: std::shared_ptr Buffer; ///< Buffer providing values for \p this object T Value; ///< The current value of the iterator }; ///\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 iterator of \c std::tuple. /// /// The class can be instantiated with a range of iterator of \c std::tuple and /// provides an interface for accessing elements of the iterated tuples one by /// one. /// /// \note The class is utilized by \c SplittedElementIterator. /// /// \tparam TupleIterator the iterator type that provides values of \c /// std::tuple /// /// \todo Consider thread safety of the class. template class TupleIteratorBuffer { public: /// Type alias for the value type of \p TupleIterator. /// \note The type is expected to be \c std::tuple. using iterator_value_type = typename TupleIterator::value_type; /// The number of elements of \c iterator_value_type. static constexpr size_t iterator_value_size = std::tuple_size_v; /// Template type alias to get element types of \c iterator_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 /// iterator_value_type. using element_idx_seq_t = seq_t; /// Creates a new instance. /// /// \param Begin the beginning of the iterator range to buffer /// \param End the end of the iterator range to buffer TupleIteratorBuffer(TupleIterator &&Begin, const TupleIterator &End) noexcept : Iterator(Begin), End(End), Buffer() {} /// Tells whether there is any more value in the handled range for index \p I. /// /// \tparam I the index of the element to check /// /// \return whether there is more value for index \p I template bool hasMore(void) const noexcept { const auto &ElementBuffer = std::get(Buffer); // There is more value if some has already been buffered or the end of the // handled range is not reached yet. return !ElementBuffer.empty() || Iterator != End; } /// Gives the next value from the handled range for index \p I. /// /// \note The function should not be called if there is no more value for /// index \p I (i.e., \c hasMore() returns \c false). If it is called in /// that situation, it returns the default value of \c element_type. /// /// \tparam I the index of the element to fetch next value for /// /// \return the next value for index \p I template element_type next(void) noexcept { auto &ElementBuffer = std::get(Buffer); if (ElementBuffer.empty()) { // This position needs a new value from Iterator if there is more. if (Iterator != End) { // Push next value for all elements. push(*Iterator++, element_idx_seq_t()); } else { // No more values; called when hasMore() is false. // Return default value. return element_type(); } } // Buffer is not empty here, return the next value. ASSERT(!ElementBuffer.empty() && "Unexpected condition"); // Get the next value and also pop it from the buffer. const element_type Elem = ElementBuffer.front(); ElementBuffer.pop(); return Elem; } private: /// Buffers next tuple value elementwise into element containers. /// /// \tparam S0 indices for accessing elements of \c std::tuple /// /// \param Tuple the values to buffer /// /// \note The second parameter provides indices as template parameter \p S0 /// and its actual value is ignored. template void push(const iterator_value_type &Tuple, Seq) noexcept { (std::get(Buffer).push(std::get(Tuple)), ...); } TupleIterator Iterator; ///< Current input iterator const TupleIterator End; ///< End of iterator range to handle tuple_buffer_container_t Buffer; ///< Container for elementwise buffering }; /// Template type alias for iterator of an element based on buffered iterator of /// \c std::tuple. /// /// The alias utilizes \c SplittedElementIterator for iterating over the values /// provided by \p TB. /// /// \tparam TB buffer providing values to iterate /// \tparam I index of the values to iterator from \p TB template using element_iterator_t = SplittedElementIterator; /// Template type alias for iterator-range of an element based on buffered /// iterator of \c std::tuple. /// /// \tparam TB buffer providing values to iterate /// \tparam I index of the values to iterator from \p TB template using element_iterator_range_t = std::pair, element_iterator_t>; ///\defgroup ElementIteratorRanges Type converter turning a buffer of \c /// std::tuple into a \c std::tuple of corresponding \c /// element_iterator_range_t. /// ///@{ /// Template declaration. /// /// \tparam TB buffer providing values /// \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 /// TupleIteratorBuffer. /// /// \note The template is defined only when \p S is a \c rosa::Seq. /// /// Usage for a proper buffer type \c TB:\code /// typename ElementIteratorRanges::Type /// \endcode template struct ElementIteratorRanges; /// Template definition. template struct ElementIteratorRanges> { /// The converted type. using Type = std::tuple...>; }; ///@} /// Convenience template type alias for easy use of \c ElementIteratorRanges. /// /// Converts a buffer of \c std::tuple into a \c std::tuple of corresponding \c /// element_iterator_range_t. /// /// \tparam TB buffer providing values /// /// \note \p TB is expected to implement an interface matching that of \c /// TupleIteratorBuffer. template using element_iterator_ranges_t = typename ElementIteratorRanges::Type; /// Template type alias for turning an iterator of \c std::tuple into /// corresponding \c element_iterator_ranges_t. /// /// The alias utilizes \c TupleIteratorBuffer for buffering the values provided /// by \p TupleIterator. The elementwise iterators are \c /// SplittedElementIterator. /// /// \tparam TupleIterator iterator of \c std::tuple template using splitted_tuple_iterator_ranges_t = element_iterator_ranges_t>; /// Creates elementwise iterator ranges for an iterator range of \c std::tuple. /// /// \note Implementation for \c rosa::iterator::splitTupleIterator. /// /// \tparam TupleIterator iterator of \c std::tuple /// \tparam S0 indices for accessing elements of \c std::tuple /// /// \param Begin the beginning of the iterator range to handle /// \param End the end of the iterator range to handle /// /// \note The last parameter provides indices as template parameter \p S0 and /// its actual value is ignored. /// /// \return \c std::tuple of elementwise iterator ranges corresponding to \p /// Begin and \p End template splitted_tuple_iterator_ranges_t splitTupleIteratorImpl(TupleIterator &&Begin, const TupleIterator &End, Seq) noexcept { using TB = TupleIteratorBuffer; // Create a buffer in shared pointer. The buffer will be destructed once // all corresponding element iterators (created below) have reached the end of // the handled range or have been destructed. auto Buffer = std::make_shared(std::move(Begin), End); return {std::make_pair(element_iterator_t(Buffer), element_iterator_t())...}; } } // End namespace +/// Gives the beginning of a range created by \c splitTupleIterator(). +/// +/// \see rosa::iterator::splitTupleIterator() +/// +/// \note The signature of the template function uses implementation details. +/// +/// \tparam TB buffer providing values to iterate +/// \tparam I index of the values to iterator from \p TB +/// +/// \param Range range to get the beginning of +/// +/// \return beginning of \p Range +template +element_iterator_t &begin(element_iterator_range_t &Range) { + return Range.first; +} + +/// Gives the end of a range created by \c splitTupleIterator(). +/// +/// \see rosa::iterator::splitTupleIterator() +/// +/// \note The signature of the template function uses implementation details. +/// +/// \tparam TB buffer providing values to iterate +/// \tparam I index of the values to iterator from \p TB +/// +/// \param Range range to get the end of +/// +/// \return end of \p Range +template +element_iterator_t &end(element_iterator_range_t &Range) { + return Range.second; +} + /// Creates elementwise iterator ranges for an iterator range of \c std::tuple. /// /// \note The implementation utilizes \c splitTupleIteratorImpl. /// /// Obtain element iterator ranges for an iterator range \c Begin and \c End of /// iterator type \c TupleIterator of \c std::tuple as \code /// auto [T1Range, T2Range, T3Range] = splitTupleIterator(std::move(Begin), End); /// auto [T1Begin, T1End] = T1Range; /// auto [T2Begin, T2End] = T2Range; /// auto [T3Begin, T3End] = T3Range; /// \endcode +/// One range can also be dissected by dedicated functions like \code +/// auto T1Begin = begin(T1Range); +/// auto T1End = end(T1Range); +/// \endcode /// /// The function returns a tuple of ranges (i.e., \c std::pair of iterators), /// which can be assigned to variables using structured binding. The ranges can /// be dissected into begin and end iterators by separate structured bindings. /// /// The function moves its first argument (i.e., \p Begin 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. /// /// \tparam TupleIterator iterator of \c std::tuple /// /// \param Begin the beginning of the iterator range to handle /// \param End the end of the iterator range to handle /// /// \return \c std::tuple of elementwise iterator ranges corresponding to \p /// Begin and \p End template splitted_tuple_iterator_ranges_t splitTupleIterator(TupleIterator &&Begin, const TupleIterator &End) noexcept { return splitTupleIteratorImpl( std::move(Begin), End, seq_t>()); } } // End namespace iterator } // End namespace rosa #endif // ROSA_SUPPORT_ITERATOR_SPLIT_TUPLE_ITERATOR_HPP diff --git a/include/rosa/support/writer/split_tuple_writer.hpp b/include/rosa/support/writer/split_tuple_writer.hpp index 22e3a35..d98ace2 100644 --- a/include/rosa/support/writer/split_tuple_writer.hpp +++ b/include/rosa/support/writer/split_tuple_writer.hpp @@ -1,554 +1,562 @@ //===-- 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 + Flush, ///< flush incomplete tuples, use default value for missing elements +#ifndef NDEBUG + DbgFail ///< fail with an assertion, available only in debug build +#endif // !defined NDEBUG }; /// 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; +#ifndef NDEBUG + case DbgFail: + ASSERT(empty(element_idx_seq_t()) && "Non-empty buffer"); + break; +#endif // !defined NDEBUG } } } /// 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