Page MenuHomePhorge

No OneTemporary

Size
51 KB
Referenced Files
None
Subscribers
None
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 <iterator>
#include <memory>
#include <set>
/// 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<DeluxeSystem> System;
/// References to all *sensors* and *agents* created by \p this object.
std::set<AgentHandle> 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<DeluxeContext>
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<MessagingSystem> 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 <typename T>
AgentHandle createSensor(const std::string &Name,
DeluxeSensor::D<T> &&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 <typename T, typename... As>
AgentHandle createAgent(const std::string &Name,
DeluxeAgent::D<T, As...> &&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 <typename Iterator, typename T = typename Iterator::value_type>
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 <typename T>
AgentHandle DeluxeContext::createSensor(const std::string &Name,
DeluxeSensor::D<T> &&F) noexcept {
AgentHandle H = System->createSensor<T>(Name, std::move(F));
DeluxeUnits.emplace(H);
return H;
}
template <typename T, typename... As>
AgentHandle DeluxeContext::createAgent(const std::string &Name,
DeluxeAgent::D<T, As...> &&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 <typename T> 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<T>::Type
+/// \endcode
+template <typename T> struct UnwrapSensorType;
+
+/// Implementation for the general case.
+template <typename T> struct UnwrapSensorType {
+ /// The type to use is the \p T itself.
+ using Type = T;
+};
+/// Template specialization for \c std::tuple.
template <typename... Ts> struct UnwrapSensorType<std::tuple<Ts...>> {
+ /// The type to use is the type of the first element of the tuple
using Type = typename std::tuple_element<0, std::tuple<Ts...>>::type;
};
+///@}
+
+/// Convenience template alias to use \c UnwrapSensorType easily.
+template <typename T>
+using sensor_t = typename UnwrapSensorType<T>::Type;
+
} // End namespace
template <typename Iterator, typename T>
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<T, typename Iterator::value_type>::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<BuiltinTypes, T>::Value;
if constexpr (!isBuiltin) {
// T must be a std::tuple.
STATIC_ASSERT(std::tuple_size<T>::value == 1, "Wrong tuple type");
STATIC_ASSERT(
(TypeListContains<BuiltinTypes,
typename std::tuple_element<0, T>::type>::Value),
"Wrong element type in tuple");
}
- using TT = typename UnwrapSensorType<T>::Type;
+ using TT = sensor_t<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<TT>::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<TT>([=](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<BuiltinTypes, T>::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 <istream>
+#include <type_traits>
#include <sstream>
-#include <vector>
-#include <map>
-#include <algorithm>
-#include <set>
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 <typename T,
bool IsSignedInt =
(std::is_integral<T>::value && std::is_signed<T>::value),
bool IsUnsignedInt =
(std::is_integral<T>::value && std::is_unsigned<T>::value),
bool IsFloat = std::is_floating_point<T>::value,
bool IsString = std::is_same<T, std::string>::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 <typename T>
struct ValueParser<T, true, false, false, false> {
STATIC_ASSERT((std::is_integral<T>::value && std::is_signed<T>::value),
"wrong type"); // Sanity check.
static T parse(const std::string &Cell) noexcept {
return static_cast<T>(std::stoll(Cell));
}
};
+/// Template specialization for unsigned integral types.
template <typename T>
struct ValueParser<T, false, true, false, false> {
STATIC_ASSERT((std::is_integral<T>::value && std::is_unsigned<T>::value),
"wrong type"); // Sanity check.
static T parse(const std::string &Cell) noexcept {
return static_cast<T>(std::stoull(Cell));
}
};
+/// Template specialization for floating-point types.
template <typename T>
struct ValueParser<T, false, false, true, false> {
STATIC_ASSERT((std::is_floating_point<T>::value),
"wrong type"); // Sanity check.
static T parse(const std::string &Cell) noexcept {
return static_cast<T>(std::stold(Cell));
}
};
+/// Template specialization for \c std::string.
template <typename T>
struct ValueParser<T, false, false, false, true> {
STATIC_ASSERT((std::is_same<T, std::string>::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 <typename... Ts> 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 <size_t... S0>
void parseRow(std::stringstream &LineStream, Seq<S0...>) {
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<S0>(Data) = ValueParser<Ts>::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<sizeof...(Ts)>());
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<Ts...> &tuple(void) const noexcept { return Data; }
private:
std::tuple<Ts...> 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<std::string> 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 <typename... Ts>
std::istream &operator>>(std::istream &Str, CSVRow<Ts...> &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 <typename... Ts> 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<Ts...> value_type; ///< Type of values iterated over.
typedef std::size_t difference_type; ///< Type to identify distance.
typedef std::tuple<Ts...> *pointer; ///< Pointer to the type iterated over.
typedef std::tuple<Ts...>
&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<Ts...> &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<Ts...> *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<Ts...> 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 <memory>
+#include <tuple>
+#include <type_traits>
+#include <queue>
+
+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 <typename TB, size_t I>
+class SplittedElementIterator {
+public:
+ /// Type alias for the values the iterator iterates.
+ using T = typename TB::template element_type<I>;
+
+ /// \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<TB> 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<I>()) {
+ Value = Buffer->template next<I>();
+ } 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<TB, I> 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<TB> 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<Tuple>::Type
+/// \endcode
+template <typename T> struct TupleBufferContainer;
+
+/// Template definition for \c std::tuple.
+template <typename... Ts> struct TupleBufferContainer<std::tuple<Ts...>> {
+ /// The converted type.
+ using Type = std::tuple<std::queue<Ts>...>;
+};
+
+///@}
+
+/// 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 <typename Tuple>
+using tuple_buffer_container_t = typename TupleBufferContainer<Tuple>::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 <typename TupleIterator>
+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<iterator_value_type>;
+
+ /// Template type alias to get element types of \c iterator_value_type by
+ /// index.
+ /// \tparam I the index of the element
+ template <size_t I>
+ using element_type =
+ typename std::tuple_element<I, iterator_value_type>::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 <size_t I> bool hasMore(void) const noexcept {
+ const auto &ElementBuffer = std::get<I>(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<I>() returns \c false). If it is called in
+ /// that situation, it returns the default value of \c element_type<I>.
+ ///
+ /// \tparam I the index of the element to fetch next value for
+ ///
+ /// \return the next value for index \p I
+ template <size_t I> element_type<I> next(void) noexcept {
+ auto &ElementBuffer = std::get<I>(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<iterator_value_size>());
+ } else {
+ // No more values; called when hasMore<I>() is false.
+ // Return default value.
+ return element_type<I>();
+ }
+ }
+ // 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<I> 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 <size_t... S0>
+ void push(const iterator_value_type &Tuple, Seq<S0...>) noexcept {
+ (std::get<S0>(Buffer).push(std::get<S0>(Tuple)), ...);
+ }
+
+ TupleIterator Iterator; ///< Current input iterator
+ const TupleIterator End; ///< End of iterator range to handle
+ tuple_buffer_container_t<iterator_value_type>
+ 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 <typename TB, size_t I>
+using element_iterator_t = SplittedElementIterator<TB, I>;
+
+/// 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 <typename TB, size_t I>
+using element_iterator_range_t =
+ std::pair<element_iterator_t<TB, I>, element_iterator_t<TB, I>>;
+
+///\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<TB, seq_t<TB::iterator_value_size>>::Type
+/// \endcode
+template <typename TB, typename S> struct ElementIteratorRanges;
+
+/// Template definition.
+template <typename TB, size_t... S0>
+struct ElementIteratorRanges<TB, Seq<S0...>> {
+ /// The converted type.
+ using Type = std::tuple<element_iterator_range_t<TB, S0>...>;
+};
+
+///@}
+
+/// 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 <typename TB>
+using element_iterator_ranges_t =
+ typename ElementIteratorRanges<TB, seq_t<TB::iterator_value_size>>::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 <typename TupleIterator>
+using splitted_tuple_iterator_ranges_t =
+ element_iterator_ranges_t<TupleIteratorBuffer<TupleIterator>>;
+
+/// 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 <typename TupleIterator, size_t... S0>
+splitted_tuple_iterator_ranges_t<TupleIterator>
+splitTupleIteratorImpl(TupleIterator &&Begin, const TupleIterator &End,
+ Seq<S0...>) noexcept {
+ using TB = TupleIteratorBuffer<TupleIterator>;
+ // 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<TB>(std::move(Begin), End);
+ return {std::make_pair(element_iterator_t<TB, S0>(Buffer),
+ element_iterator_t<TB, S0>())...};
+}
+
+} // 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<T1, T2, T3> 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 <typename TupleIterator>
+splitted_tuple_iterator_ranges_t<TupleIterator>
+splitTupleIterator(TupleIterator &&Begin, const TupleIterator &End) noexcept {
+ return splitTupleIteratorImpl(
+ std::move(Begin), End,
+ seq_t<std::tuple_size_v<typename TupleIterator::value_type>>());
+}
+
+} // 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"

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jul 3, 1:43 AM (1 d, 6 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
157175
Default Alt Text
(51 KB)

Event Timeline