diff --git a/include/rosa/deluxe/DeluxeContext.hpp b/include/rosa/deluxe/DeluxeContext.hpp index 646e866..ed34f14 100755 --- a/include/rosa/deluxe/DeluxeContext.hpp +++ b/include/rosa/deluxe/DeluxeContext.hpp @@ -1,333 +1,365 @@ //===-- rosa/deluxe/DeluxeContext.hpp ---------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeContext.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Public interface for the *deluxe interface* for working with agent /// systems. /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXECONTEXT_HPP #define ROSA_DELUXE_DELUXECONTEXT_HPP #include "rosa/deluxe/DeluxeSystem.hpp" #include "rosa/support/types.hpp" #include #include #include /// Local helper macro to log and return a /// \c rosa::deluxe::DeluxeContext::ErrorCode value. /// /// Creates a debug message with the stringified value and returns the value. /// /// \param Err \c rosa::deluxe::DeluxeContext::ErrorCode value to log and /// return #define DCRETERROR(Err) \ { \ LOG_DEBUG(#Err); \ return Err; \ } namespace rosa { namespace deluxe { /// Defines the *deluxe interface*. class DeluxeContext { /// A system owned by \p this object. /// /// \note The reference is kept in a \c std::shared_ptr because of the member /// function \c rosa::deluxe::DeluxeContext::getSystem. std::shared_ptr System; /// References to all *sensors* and *agents* created by \p this object. std::set DeluxeUnits; public: /// Errors that may be resulted by some of the member functions of the class. enum struct ErrorCode { NoError, TypeMismatch, NotSensor, NotAgent, WrongPosition, AlreadyHasSlave, AlreadyHasMaster, AlreadyHasValueStream }; /// Returns a new instance of \c rosa::deluxe::DeluxeContext. /// /// \param Name name of the underlying \c rosa::DeluxeSystem /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::deluxe::DeluxeContext with a new, empty \c rosa::DeluxeSystem static std::unique_ptr create(const std::string &Name) noexcept; private: /// Creates a new instance. /// /// \note Private constructor restricts instantiation to member functions of /// the class. /// /// \param Name name of the underlying \c rosa::MessagingSystem DeluxeContext(const std::string &Name) noexcept; public: /// Destroys \p this object. ~DeluxeContext(void) noexcept; /// Returns a reference for the underlying \c rosa::MessagingSystem. /// /// \note One cannot do much with a \c rosa::MessagingSystem currently, this /// is for future use. /// /// \return reference for the underlying \c rosa::MessagingSystem. std::weak_ptr getSystem(void) const noexcept; /// Creates a new *sensor* in the context of \p this object. /// /// \tparam T type of data the new *sensor* operates on /// /// \todo The current implementation is an intermediate state, handles /// built-in types and std::tuple, but the latter with with one element only. /// Fix it when merging with DeluxeTuple. /// /// \param Name name of the new *sensor* /// \param F function for the new *sensor* to generate the next value with /// during normal operation /// /// \note \p F is not used during simulation, in which case /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to register /// an alternative simulation data source with /// \c rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may /// safely keep relying on the default value of \p F as long as only /// simulation of the system is to be done. /// /// \return \c rosa::AgentHandle for the new *sensor* template AgentHandle createSensor(const std::string &Name, DeluxeSensor::D &&F = [](void) { return T(); }) noexcept; /// Creates a new *agent* in the context of \p this object. /// /// \tparam T type of data the new *agent* outputs /// \tparam As types of inputs the new *agent* takes /// /// \param Name name of the new *agent* /// \param F function for the new *agent* to process input values and /// generate output with /// /// \return \c rosa::AgentHandle for the new *agent* template AgentHandle createAgent(const std::string &Name, DeluxeAgent::D &&F) noexcept; /// Connectes a *sensor* to an *agent* in the context of \p this object. /// /// \param Agent the *agent* to connect to /// \param Pos the index of slot of \p Agent to connect \p Sensor to /// \param Sensor the *sensor* to connect /// \param Description optional textual description of the connection /// /// \return how successfull connecting \p Sensor to \p Agent at slot index /// \p Pos was /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotAgent` | Referred \p Agent is not \c rosa::deluxe::DeluxeAgent /// `NotSensor` | Referred \p Sensor is not \c rosa::deluxe::DeluxeSensor /// `WrongPosition` | \p Pos is not a valid input position of \p Agent /// `TypeMismatch` | Expected input type at position \p Pos of \p Agent is other than the output type of \p Sensor /// `AlreadyHasSlave` | \p Agent at position \p Pos already has a *slave* registered /// `AlreadyHasMaster` | \p Sensor already has a *master* registered ErrorCode connectSensor(AgentHandle Agent, const size_t Pos, AgentHandle Sensor, const std::string &Description = "") noexcept; /// Connectes two *agents* in the context of \p this object. /// /// \param Master the *agent* to connect to /// \param Pos the index of slot of \p Master to connect \p Slave to /// \param Slave the *agent* to connect /// \param Description optional textual description of the connection /// /// \return how succesfull connecting \p Slave to \p Master at slot index /// \p Pos was /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotAgent` | Referred \p Master or \p Slave is not \c rosa::deluxe::DeluxeAgent /// `WrongPosition` | \p Pos is not a valid input position of \p Master /// `TypeMismatch` | Expected input type at position \p Pos of \p Master is other than the output type of \p Slave /// `AlreadyHasSlave` | \p Master at position \p Pos already has a *slave* registered /// `AlreadyHasMaster` | \p Slave already has a *master* registered ErrorCode connectAgents(AgentHandle Master, const size_t Pos, AgentHandle Slave, const std::string &Description = "") noexcept; /// Initializes \c this object and others managed by \p this object for /// setting up and performing simulation. /// /// \see \c rosa::deluxe::DeluxeContext::registerSensorValues, /// \c rosa::deluxe::DeluxeContext::simulate /// /// Need to clear simulation data sources from all the *sensors*. void initializeSimulation(void) noexcept; /// Registers a stream providing values for a *sensor* during simulation. /// /// \tparam Iterator type of iterator providing values for \p Sensor /// \tparam T type of values \p Sensor is operating on, always use default! /// /// \param Sensor the *sensor* to register values for /// \param Start provides values for \p Sensor /// \param End denotes the end of stream of values /// \param Default value to be used when input stream is depleted during /// simulation /// /// \return how successful registering \p Source for \p Sensor /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `TypeMismatch` | \p Sensor generates values of a type other than \p T /// `NotSensor` | Referred \p Sensor is not \c rosa::deluxe::DeluxeSensor /// `AlreadyHasValueStream` | \p Sensor already has simulation data source set template ErrorCode registerSensorValues(AgentHandle Sensor, Iterator &&Start, const Iterator &End, T Default = {}) noexcept; /// Performs the system contained by \p this object. /// /// The function performs \p NumCycles cycle of simulation. In each cycle, /// all the *agents* and *sensors* registered in /// \c rosa::deluxe::DeluxeContext::DeluxeUnits are trigged for execution. /// /// \param NumCycles number of cycles to perform /// /// \pre All the *sensors* in the system contained by \p this object generate /// their output from simulation data sources. void simulate(const size_t NumCycles) const noexcept; }; template AgentHandle DeluxeContext::createSensor(const std::string &Name, DeluxeSensor::D &&F) noexcept { AgentHandle H = System->createSensor(Name, std::move(F)); DeluxeUnits.emplace(H); return H; } template AgentHandle DeluxeContext::createAgent(const std::string &Name, DeluxeAgent::D &&F) noexcept { AgentHandle H = System->createAgent(Name, std::move(F)); DeluxeUnits.emplace(H); return H; } /// Anonymous namespace for helper facilities, consider it private. namespace { -/// \todo Document... -template struct UnwrapSensorType { using Type = T; }; +///\defgroup UnwrapSensorType Type helper for implementing \c +/// rosa::deluxe::DeluxeContext::registerSensorValue() +/// +///@{ + +/// Template declaration. +/// +/// \tparam T type to obtain matching sensor type for +/// +/// Obtain a sensor type for a type \p T by: \code +/// typename UnwrapSensorType::Type +/// \endcode +template struct UnwrapSensorType; + +/// Implementation for the general case. +template struct UnwrapSensorType { + /// The type to use is the \p T itself. + using Type = T; +}; +/// Template specialization for \c std::tuple. template struct UnwrapSensorType> { + /// The type to use is the type of the first element of the tuple using Type = typename std::tuple_element<0, std::tuple>::type; }; +///@} + +/// Convenience template alias to use \c UnwrapSensorType easily. +template +using sensor_t = typename UnwrapSensorType::Type; + } // End namespace template DeluxeContext::ErrorCode DeluxeContext::registerSensorValues(AgentHandle Sensor, Iterator &&Start, const Iterator &End, T Default) noexcept { // Get the type of values provided by \p Iterator. STATIC_ASSERT((std::is_same::value), "type mismatch"); + // \note This constexpr variable is defined in the lambda below for MSVC. + // Keep that definition in sync with this one. constexpr bool isBuiltin = TypeListContains::Value; if constexpr (!isBuiltin) { // T must be a std::tuple. STATIC_ASSERT(std::tuple_size::value == 1, "Wrong tuple type"); STATIC_ASSERT( (TypeListContains::type>::Value), "Wrong element type in tuple"); } - using TT = typename UnwrapSensorType::Type; + using TT = sensor_t; // Make sure preconditions are met. if (!System->isDeluxeSensor(Sensor)) { DCRETERROR(ErrorCode::NotSensor); } auto S = System->getDeluxeSensor(Sensor); ASSERT(S); // Sanity check. if (S->OutputType != TypeNumberOf::Value) { DCRETERROR(ErrorCode::TypeMismatch); } else if (S->simulationDataSourceIsSet()) { DCRETERROR(ErrorCode::AlreadyHasValueStream); } // Register input stream. // \note Need to capture parameters by value so having local copies. S->registerSimulationDataSource( DeluxeSensor::D([=](void) mutable noexcept { +#ifdef ROSA_WINDOWS + // MSVC has problem propagating constexpr into the lambda; repeat it. + // Keep this definition in sync with the original above. + constexpr bool isBuiltin = TypeListContains::Value; +#endif // defined ROSA_WINDOWS if (Start != End) { TT Value; if constexpr (isBuiltin) { Value = *Start; } else { Value = std::get<0>(*Start); } ++Start; LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName << "': " << Value << '\n'; return Value; } else { TT Value; if constexpr (isBuiltin) { Value = Default; } else { Value = std::get<0>(Default); } LOG_TRACE_STREAM << "Providing default value for sensor '" << S->FullName << "': " << Value << '\n'; return Value; } })); return ErrorCode::NoError; } } // End namespace deluxe } // End namespace rosa // Undef local macro if not used in the corresponding implementation. #ifndef ROSA_LIB_DELUXE_DELUXECONTEXT_CPP #undef DCRETERROR #endif #endif // ROSA_DELUXE_DELUXECONTEXT_HPP diff --git a/include/rosa/support/csv/CSVReader.hpp b/include/rosa/support/csv/CSVReader.hpp index f4788f3..0df2355 100755 --- a/include/rosa/support/csv/CSVReader.hpp +++ b/include/rosa/support/csv/CSVReader.hpp @@ -1,485 +1,492 @@ //===-- rosa/support/csv/CSVReader.hpp --------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/support/csv/CSVReader.hpp /// /// \authors David Juhasz (david.juhasz@tuwien.ac.at), Edwin Willegger (edwin.willegger@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Facitilities to read CSV files. /// /// \note The implementation is based on the solution at /// https://stackoverflow.com/a/1120224 /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_CSV_CSVREADER_HPP #define ROSA_SUPPORT_CSV_CSVREADER_HPP #include "rosa/support/debug.hpp" #include "rosa/support/sequence.hpp" #include +#include #include -#include -#include -#include -#include namespace rosa { namespace csv { /// Indicating it the CSV file contains any header or not enum class HeaderInformation { HasHeader, HasNoHeader }; /// Anonymous namespace providing implementation details for /// \c rosa::csv::CSVIterator, consider it private. namespace { +///\defgroup ValueParser Helper template struct to parse a value +/// +///@{ + /// Provides facility for parsing one value from a string. /// /// \tparam T type of value to parse /// \tparam IsSignedInt if \p T is a signed integral type, always use default /// \tparam IsUnsignedInt if \p T is an unsigned integral type, always use /// default /// \tparam IsFloat if \p T is a floating-point type, always use default /// \tparam IsString if \p T is \c std::string, always use default /// /// \note Specializations of this struct are provided for arithmentic types /// and \c std::string. template ::value && std::is_signed::value), bool IsUnsignedInt = (std::is_integral::value && std::is_unsigned::value), bool IsFloat = std::is_floating_point::value, bool IsString = std::is_same::value> struct ValueParser { - /// + /// Parses one value from \p Cell /// /// \param Cell the \c std::string to parse /// /// \return the parsed value /// /// \note The function silently fails if cannot parse \p Cell for type \p T. static T parse(const std::string &Cell) noexcept; }; +/// Template specialization for signed integral types. template struct ValueParser { STATIC_ASSERT((std::is_integral::value && std::is_signed::value), "wrong type"); // Sanity check. static T parse(const std::string &Cell) noexcept { return static_cast(std::stoll(Cell)); } }; +/// Template specialization for unsigned integral types. template struct ValueParser { STATIC_ASSERT((std::is_integral::value && std::is_unsigned::value), "wrong type"); // Sanity check. static T parse(const std::string &Cell) noexcept { return static_cast(std::stoull(Cell)); } }; +/// Template specialization for floating-point types. template struct ValueParser { STATIC_ASSERT((std::is_floating_point::value), "wrong type"); // Sanity check. static T parse(const std::string &Cell) noexcept { return static_cast(std::stold(Cell)); } }; +/// Template specialization for \c std::string. template struct ValueParser { STATIC_ASSERT((std::is_same::value), "wrong type"); // Sanity check. static T parse(const std::string &Cell) noexcept { return Cell; } }; +///@} + /// Parses and stores entries from a row of CSV data. /// /// \tparam Ts types of values to parse and store, i.e. entries in the row /// /// \note The implementation relies on \c rosa::csv::CSVRowParser, which is /// implemented only for `arithmetic` types -- signed and unsigned integral /// and floating-point types -- and for \c std::string. Those are the valid /// values for \p Ts. template class CSVRow { private: /// Parses a given row of CSV data into \c CSVRow::Data. /// /// \ CSVRow::Data is filled with values parsed from \p LineStream. Entries /// in the line are to be separated by commas, the character `,`. /// /// \note Parsed values are silently converted to types \p Ts. /// /// \note Parsing silently fails if values do not match \p Ts. /// /// \tparam S0 indices to access tuple elements. /// /// \param [in,out] LineStream the line to parse /// /// \note The last argument is used only to get \p S0, the actual value of /// the parameter is ignored. template void parseRow(std::stringstream &LineStream, Seq) { STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "Not matching template arguments."); std::string Cell; // Get fields and parse the values into the proper element of the tuple // one by one in a fold expression. ((std::getline(LineStream, Cell, ','), std::get(Data) = ValueParser::parse(Cell)), ...); } public: /// Constructor with all possible parameters /// /// The function creates an instance of an CSVRow object and sets the attributes of the /// object to the values of the parameters. /// /// \param SkipRows the number of data rows to skip, not taking header into account. /// \param HeaderInfo is the first line of the file a header row or not. /// \param Delimeter to seperate between the data entries within one row. CSVRow(const size_t SkipRows = 0, const HeaderInformation HeaderInfo = HeaderInformation::HasHeader, const char Delimeter = ',') : SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimeter(Delimeter), RowNumber(0), IsHeaderRead(false) { } /// Parses and stores one row of CSV data. /// /// The function reads one line from \p Str and parses it into /// \c rosa::csv::CSVRow::Data using \c rosa::csv::CSVRowParser. /// /// \param [in,out] Str input stream of a CSV file void readNextRow(std::istream &Str) noexcept { std::string Line; std::getline(Str, Line); if(Line.size() > 0){ std::stringstream LineStream(Line); parseRow(LineStream, seq_t()); RowNumber = RowNumber + 1; } } /// Read header row and stores it as \p std::string. /// /// The function reads the first line of the csv file and stores the entries /// in a vector. /// /// \param [in,out] Str input stream of a CSV file void readHeader(std::istream &Str) noexcept { std::string Line; std::getline(Str, Line); std::stringstream LineStream(Line); std::string Value; while( getline(LineStream, Value, Delimeter) ){ Header.push_back(Value); } IsHeaderRead = true; } /// The number of rows to skip once. /// /// This function returns the number of data rows to skip /// at the beginning of the file. /// /// \return The number of rows to skip at the beginning of a csv file. inline size_t SkipNumRows() const noexcept { return this->SkipRows; } /// The current row number within the csv file. /// /// This function returns the current row number. The header /// row is not counted as a row. /// /// \returns the current row number within the csv file. inline size_t CurRow() const noexcept { return this->RowNumber; } /// Indiciates if the header was already read. /// /// This function returns true, if the header of a csv file which contains /// a header file is already read. /// The user has to pass in the attribute HeaderInfo the information if the /// file has in the first row the header row or not. /// /// \return if the header of a file is already read. inline bool IsHeaderReadDone() const noexcept{ return this->IsHeaderRead; } /// Indicates if the file contains a header row in the first row. /// /// This function returns if the file contains a header row. /// The information if the file contains a header row or not, has to be passed by the user. /// The standard value is HeaderInformation::HasHeader /// /// \return if the csv file contains a header row in the first line of the file. inline HeaderInformation HasFileHeader() const noexcept { return this->HeaderInfo; } /// Set the number of rows to skip. /// /// This function sets the number of rows to skip at the beginning of /// the reading of the file. /// /// \param SkipRows the number of rows you want to skip at the beginning of the file. inline void SetSkipRows(const size_t SkipRows) noexcept { this->SkipRows = SkipRows; } /// Is the first row a header row or not. /// /// This function sets the information, if the first row of the csv file /// is a header line or not. /// /// \param HeaderInfo if the first row is a header row or not. inline void SetHeaderInfo(const HeaderInformation HeaderInfo) noexcept { this->HeaderInfo = HeaderInfo; } /// Set the seperator between data entries. /// /// This funcction sets the separator between the data entries of the csv file. /// /// \param Delimeter the character that separates the data values. inline void SetDelimeter(char Delimeter) { this->Delimeter = Delimeter; } /// Gives a constant references for the \c std::tuple containing the values /// read by \p this object. /// /// \return \c CSVRow::Data const std::tuple &tuple(void) const noexcept { return Data; } private: std::tuple Data; ///< Stores parsed entries size_t RowNumber; ///< Current row number, counts all row numbers including the header row. HeaderInformation HeaderInfo; ///< If the file contains a header row or not. size_t SkipRows; ///< The number of rows to skip at the very beginning of the file. ///< This number only applies on the number of data rows. ///< If your file contains a header row and data rows, the skiping ///< of the header row is not taken into account. std::vector Header; ///< The content of the header row. bool IsHeaderRead; ///< Was the header read or not. char Delimeter; ///< The seperator between the data entries. }; /// Reads a row of CSV data into \c rosa::csv::CSVRow. /// /// The next line is read from \p Str by calling /// \c rosa::csv::CSVRow::readNextRow on \p Data until all lines are /// skipped. /// /// If the function is called for the first time and the file contains /// a header than is the header and the first data row read in after the /// number of rows that the user wants to skip. /// /// \tparam Ts type of values to read from the row /// /// \note The CSV file should contain a line with fields matching \p Ts... /// /// \param [in,out] Str input stream of a CSV file /// \param [in,out] Data object to read the next line into /// /// \return \p Str after reading one line from it template std::istream &operator>>(std::istream &Str, CSVRow &Data) { if( Data.HasFileHeader() == HeaderInformation::HasHeader && !Data.IsHeaderReadDone() ) { Data.readHeader(Str); } while(Data.CurRow() < (Data.SkipNumRows())){ Data.readNextRow(Str); } //read the lines after you skipped the number of rows you want to skip Data.readNextRow(Str); return Str; } } // End namespace /// Provides `InputIterator` features for iterating over a CSV file. /// /// The iterator parses rows into `std::tuple` values and iterates over the /// file row by row. /// /// \tparam Ts types of values stored in one row of the CSV file /// /// \note The iterator expects each row to consists of fields matching \p Ts. /// /// \note The implementation relies on \c rosa::csv::CSVRow, which in turn /// relies on \c rosa::csv::CSVRowParser, which is implemented only for /// `arithmetic` types -- signed and unsigned integral types and floating-point /// types -- and for \c std::string. Those are the valid values for \p Ts template class CSVIterator { public: /// \defgroup CSVIteratorTypedefs Typedefs of rosa::csv::CSVIterator /// /// Standard `typedef`s for iterators. /// ///@{ typedef std::input_iterator_tag iterator_category; ///< Category of the iterator. typedef std::tuple value_type; ///< Type of values iterated over. typedef std::size_t difference_type; ///< Type to identify distance. typedef std::tuple *pointer; ///< Pointer to the type iterated over. typedef std::tuple &reference; ///< Reference to the type iterated over. ///@} /// Creates a new instance. /// /// \param [in,out] S input stream to iterate over /// \param SkipRows the number of rows you want to skip only once at the beginning of the file. /// If you have an header in the file, it is supposed to be the first row, and it will be always read out. /// But after this header the next number of Rows will be skipped. /// \param HeaderInfo is used to know wheter the file contains an header row or not. /// The header has to be in the first row. /// \param Delimeter is the separator between the differnt values of the csv file. CSVIterator(std::istream &S, const size_t SkipRows = 0, const HeaderInformation HeaderInfo = HeaderInformation::HasHeader, const char Delimeter = ',') : Str(S.good() ? &S : nullptr), Row(), SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimeter(Delimeter){ Row.SetSkipRows(SkipRows); Row.SetHeaderInfo(HeaderInfo); Row.SetDelimeter(Delimeter); // \c rosa::csv::CSVIterator::Row is initialized empty so the first // incrementation here will read the first row. ++(*this); } /// Creates an empty new instance. CSVIterator(void) noexcept : Str(nullptr) {} /// Pre-increment operator. /// /// The implementation reads the next row. If the end of the input stream is /// reached, the operator becomes empty and has no further effect. /// /// \return \p this object after incrementing it. CSVIterator &operator++() { if (Str) { if (!((*Str) >> Row)) { Str = nullptr; } } 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. CSVIterator operator++(int) { CSVIterator Tmp(*this); ++(*this); return Tmp; } /// Returns a constant reference to the current entry. /// /// \note Should not dereference the iterator when it is empty. /// /// \return constant reference to the current entry. const std::tuple &operator*(void)const noexcept { return Row.tuple(); } /// Returns a constant pointer to the current entry. /// /// \note Should not dereference the iterator when it is empty. /// /// \return constant pointer to the current entry. const std::tuple *operator->(void)const noexcept { return &Row.tuple(); } /// Tells if \p this object is equal to another one. /// /// Two \c rosa::csv::CSVReader 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 CSVIterator &RHS) const noexcept { return ((this == &RHS) || ((this->Str == nullptr) && (RHS.Str == nullptr))); } /// Tells if \p this object is not equal to another one. /// /// \see rosa::csv::CSVReader::operator== /// /// \param RHS other object to compare to /// /// \return whether \p this object is not equal with \p RHS. bool operator!=(const CSVIterator &RHS) const noexcept { return !((*this) == RHS); } /// Set the delimeter used in the csv file. /// \param Delimeter the character which separates the values in the csv file. inline void setDelimeter(char Delimeter) noexcept { this->Delimeter = Delimeter; } /// get the delimeter 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 getDelimeter() const noexcept { return this->Delimeter; } private: std::istream *Str; ///< Input stream of a CSV file to iterate over. CSVRow Row; ///< Content of the current row char Delimeter; ///< Delimeter between the entries in the csv file. HeaderInformation HeaderInfo; ///< does the csv file contain a header or not, if this information is ///< not given correclty, the reading of the header would result in ///< in an error. size_t SkipRows; ///< Number of Rows to skip only once at the beginning of the file. }; } // End namespace csv } // End namespace rosa #endif // ROSA_SUPPORT_CSV_CSVREADER_HPP diff --git a/include/rosa/support/iterator/namespace.h b/include/rosa/support/iterator/namespace.h new file mode 100644 index 0000000..027978d --- /dev/null +++ b/include/rosa/support/iterator/namespace.h @@ -0,0 +1,25 @@ +//===-- rosa/support/iterator/namespace.h -----------------------*- C++ -*-===/ +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===/ +/// +/// \file rosa/support/iterator/namespace.h +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Documentation for the namespace \c rosa::iterator. +/// +//===----------------------------------------------------------------------===/ + +#ifndef ROSA_SUPPORT_ITERATOR_NAMESPACE_H +#define ROSA_SUPPORT_ITERATOR_NAMESPACE_H + +namespace rosa { +/// Provides facilities to work with iterators. +namespace iterator {} +} // End namespace rosa + +#endif // ROSA_SUPPORT_ITERATOR_NAMESPACE_H diff --git a/include/rosa/support/iterator/split_tuple_iterator.hpp b/include/rosa/support/iterator/split_tuple_iterator.hpp new file mode 100644 index 0000000..f64b346 --- /dev/null +++ b/include/rosa/support/iterator/split_tuple_iterator.hpp @@ -0,0 +1,427 @@ +//===-- 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; + + /// 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++, 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 + +/// 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 +/// +/// 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 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/lib/support/CMakeLists.txt b/lib/support/CMakeLists.txt index 6e191ef..12a3334 100644 --- a/lib/support/CMakeLists.txt +++ b/lib/support/CMakeLists.txt @@ -1,38 +1,42 @@ 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 ) diff --git a/lib/support/iterator/namespace.cpp b/lib/support/iterator/namespace.cpp new file mode 100644 index 0000000..57fe49f --- /dev/null +++ b/lib/support/iterator/namespace.cpp @@ -0,0 +1,20 @@ +//===-- support/iterator/namespace.cpp --------------------------*- C++ -*-===/ +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===/ +/// +/// \file support/iterator/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/iterator/namespace.h. +/// +//===----------------------------------------------------------------------===/ + +#include "rosa/support/iterator/namespace.h" diff --git a/lib/support/iterator/split_tuple_iterator.cpp b/lib/support/iterator/split_tuple_iterator.cpp new file mode 100644 index 0000000..72f4f1f --- /dev/null +++ b/lib/support/iterator/split_tuple_iterator.cpp @@ -0,0 +1,20 @@ +//===-- support/iterator/split_tuple_iterator.cpp ---------------*- C++ -*-===/ +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===/ +/// +/// \file support/iterator/split_tuple_iterator.cpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Implementation for rosa/support/iterator/split_tuple_iterator.hpp. +/// +/// \note Empty implementation, source file here to have a compile database +/// entry for rosa/support/iterator/split_tuple_iterator.hpp. +/// +//===----------------------------------------------------------------------===/ + +#include "rosa/support/iterator/split_tuple_iterator.hpp"