Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F386328
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Size
51 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Jul 3, 1:43 AM (1 d, 11 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
157175
Default Alt Text
(51 KB)
Attached To
Mode
R20 SoC_Rosa_repo
Attached
Detach File
Event Timeline
Log In to Comment