Skip to content

Commit

Permalink
parse functions can now handle Cell* classes and primitive numeric ty…
Browse files Browse the repository at this point in the history
…pes in containers.

Parser::parseTo() has been renamed to Parser::parseTo2DVector() for clarity.
Parser::parseToMatrixRowMajor() and Parser::parseToMatrixColumnMajor() have been renamed to parseToVectorRowMajor() and parseToVectorColumnMajor(), respectively.
Parser::parseToArray() has been added for compile-time parsing to flat array.
A few more runtime examples have been added.
  • Loading branch information
ashaduri committed Dec 22, 2023
1 parent ac57c13 commit dd00589
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 58 deletions.
43 changes: 41 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ std::vector<std::vector<Csv::CellReference>> cell_refs;
Csv::Parser parser;

try {
// parseTo() throws Csv::ParseError on error.
parser.parseTo(data, cell_refs);
// This throws Csv::ParseError on error.
parser.parseTo2DVector(data, cell_refs);
}
catch(Csv::ParseError& ex) {
std::cerr << "CSV parse error: " << ex.what() << std::endl;
Expand All @@ -63,6 +63,45 @@ std::cout << "Column 0, row 1: " << cell_refs[0][1].getDouble().value() << std::
std::cout << "Column 1, row 1: " << cell_refs[1][1].getDouble().value() << std::endl; // 6
```
### Runtime Parsing of Numeric Matrix Into 1D Vector With Row-Major Order
#### Example:
``` C++
#include "csv_parser.h"
// ...
using namespace std::string_view_literals;
// Data to parse
std::string_view data = "3,5\n10e4,20"sv;
// Let "cell_refs" be a vector double values.
std::vector<std::vector<double>> matrix_data;
Csv::Parser parser;
try {
// This throws Csv::ParseError on error.
parser.parseTo2DVector(data, matrix_data);
}
catch(Csv::ParseError& ex) {
std::cerr << "CSV parse error: " << ex.what() << std::endl;
return EXIT_FAILURE;
}
assert(matrix_data.size() == 2);
assert(matrix_data[0].size() == 2);
assert(matrix_data[1].size() == 2);
std::cout << "Column 0, row 0: " << cell_refs[0][0] << std::endl; // 3
std::cout << "Column 1, row 0: " << cell_refs[1][0] << std::endl; // 5
std::cout << "Column 0, row 1: " << cell_refs[0][1] << std::endl; // 10e4
std::cout << "Column 1, row 1: " << cell_refs[1][1] << std::endl; // 20
```

### Compile-Time Parsing

Currently, parsing at compile-time has some restrictions:
Expand Down
35 changes: 35 additions & 0 deletions csv_parser/csv_cell.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,17 @@ class CellStringValue {



/// Parser::parse*() functions use this to create the value_type object of a container.
/// By default, it handles Cell* classes and primitive numeric types.
/// This trait can be specialized for user-defined types.
template<typename CellT>
class CellTrait {
public:
/// Create an object of type CellT from cell contents represented as string_view.
[[nodiscard]] static constexpr CellT create(std::string_view cell, CellTypeHint hint);
};



/// Unescape a string - collapse every occurrence of 2 consecutive double-quotes to one.
inline std::string cleanString(std::string_view view);
Expand Down Expand Up @@ -584,6 +595,30 @@ const std::string& CellStringValue::getString() const



template<typename CellT>
constexpr CellT CellTrait<CellT>::create(std::string_view cell, CellTypeHint hint)
{
// The types here are the same as in readNumber()
if constexpr(std::is_same_v<CellT, float>
|| std::is_same_v<CellT, double>
|| std::is_same_v<CellT, long double>) {
return readNumber<CellT>(cell).value_or(std::numeric_limits<CellT>::quiet_NaN());
} else if constexpr(std::is_same_v<CellT, int>
|| std::is_same_v<CellT, long int>
|| std::is_same_v<CellT, long long int>
|| std::is_same_v<CellT, unsigned long int>
|| std::is_same_v<CellT, unsigned long long int>) {
return readNumber<CellT>(cell).value_or(0);
} else {
// Cell* classes
return CellT(cell, hint);
}
}





std::string cleanString(std::string_view view)
{
std::string s;
Expand Down
26 changes: 21 additions & 5 deletions csv_parser/csv_matrix.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace Csv {



/// Type hint associated with the cell to determine the type of the cell value
/// Order of elements in a matrix.
enum class MatrixOrder {
RowMajor, ///< A11, A12, A13, A21, ...
ColumnMajor, ///< A11, A21, A31, A12, ...
Expand All @@ -29,16 +29,32 @@ class MatrixInformation {
/// Get index in flat-matrix vector
/// \param row 0-based row number
/// \param column 0-based column number
/// \param rows Number of rows in matrix
/// \param columns Number of columns in matrix
/// \param order Matrix order
/// \return 0-based index in vector
constexpr std::size_t matrixIndex(std::size_t row, std::size_t column) const
[[nodiscard]] static constexpr std::size_t matrixIndex(std::size_t row, std::size_t column,
std::size_t rows, std::size_t columns, MatrixOrder order)
{
if (order_ == MatrixOrder::RowMajor) {
return row * columns_ + column;
if (order == MatrixOrder::RowMajor) {
return row * columns + column;
}
return column * rows_ + row;
return column * rows + row;
}



/// Get index in flat-matrix vector
/// \param row 0-based row number
/// \param column 0-based column number
/// \return 0-based index in vector
[[nodiscard]] constexpr std::size_t matrixIndex(std::size_t row, std::size_t column) const
{
return matrixIndex(row, column, rows_, columns_, order_);
}



[[nodiscard]] constexpr std::size_t getRows() const
{
return rows_;
Expand Down
69 changes: 52 additions & 17 deletions csv_parser/csv_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,17 @@ class Parser {
constexpr void parse(std::string_view data, StoreCellFunction storeCellFunc) const;


/// Parse CSV string data into a vector of columns.
/// Parse CSV string data into a vector of columns. The innermost type may be one of Cell* classes
// (e.g., CellReference), or a primitive numeric type (e.g., double).
/// \param data CSV string data.
/// \param[out] values An empty 2D vector to store the data in. Accepts types like std::vector<std::vector<CellReference>>.
/// \throws ParseError
template<typename Vector2D>
constexpr void parseTo(std::string_view data, Vector2D& values) const;
constexpr void parseTo2DVector(std::string_view data, Vector2D& values) const;


/// Parse CSV string to 2D std::array, an array of columns.
/// This method conveniently wraps parseTo() to simplify compile-time parsing.
/// This method conveniently wraps parse() function to simplify compile-time parsing.
/// \param data CSV string data.
/// \tparam rows Number of rows
/// \tparam columns Number of columns
Expand All @@ -106,7 +107,7 @@ class Parser {
/// \return matrix information
/// \throws ParseError
template<typename Vector>
constexpr MatrixInformation parseToMatrixRowMajor(std::string_view data, Vector& values) const;
constexpr MatrixInformation parseToVectorRowMajor(std::string_view data, Vector& values) const;


/// Parse CSV string data into a flat matrix in row-major format (A11, A12, A13, A21, ...).
Expand All @@ -117,7 +118,7 @@ class Parser {
/// \return matrix information
/// \throws ParseError
template<typename Vector>
constexpr MatrixInformation parseToMatrixRowMajor(std::string_view data, Vector& values,
constexpr MatrixInformation parseToVectorRowMajor(std::string_view data, Vector& values,
std::optional<std::size_t> rows_hint, std::optional<std::size_t> columns) const;


Expand All @@ -129,11 +130,24 @@ class Parser {
/// \return matrix information
/// \throws ParseError
template<typename Vector>
constexpr MatrixInformation parseToMatrixColumnMajor(std::string_view data, Vector& values,
constexpr MatrixInformation parseToVectorColumnMajor(std::string_view data, Vector& values,
std::size_t rows, std::optional<std::size_t> columns_hint) const;


/// Parse CSV string to 1D std::array, stored in row-major or column-major order.
/// This method conveniently wraps parse() function to simplify compile-time parsing.
/// \param data CSV string data.
/// \tparam rows Number of rows
/// \tparam columns Number of columns
/// \tparam Cell Type of cell in array, e.g. CellStringReference
/// \return std::array<Cell, rows*columns>
/// \throws ParseError
template<std::size_t rows, std::size_t columns, typename Cell = CellStringReference>
constexpr auto parseToArray(std::string_view data, MatrixOrder order) const;


/// A helper function to get an element of parsed 2D vector without resorting to offset calculations.
/// \tparam Vector2D a vector of columns
/// \return Vector2D's innermost type
template<typename Vector2D>
static constexpr auto vector2DValue(const Vector2D& values, std::size_t row, std::size_t column);
Expand Down Expand Up @@ -469,7 +483,7 @@ constexpr void Parser::parse(std::string_view data, StoreCellFunction storeCellF


template<typename Vector2D>
constexpr void Parser::parseTo(std::string_view data, Vector2D& values) const
constexpr void Parser::parseTo2DVector(std::string_view data, Vector2D& values) const
{
Vector2D parsed_values;
parse(data,
Expand All @@ -481,7 +495,7 @@ constexpr void Parser::parseTo(std::string_view data, Vector2D& values) const
if (parsed_values[column].size() < (row + 1)) {
parsed_values[column].resize(row + 1);
}
parsed_values[column][row] = typename Vector2D::value_type::value_type(cell_data, hint);
parsed_values[column][row] = CellTrait<typename Vector2D::value_type::value_type>::create(cell_data, hint);
}
);
std::swap(values, parsed_values);
Expand All @@ -499,7 +513,7 @@ constexpr auto Parser::parseTo2DArray(std::string_view data) const
std::string_view cell_data, Csv::CellTypeHint hint)
constexpr
{
matrix[column][row] = Cell(cell_data, hint);
matrix[column][row] = CellTrait<Cell>::create(cell_data, hint);
}
);

Expand All @@ -509,15 +523,15 @@ constexpr auto Parser::parseTo2DArray(std::string_view data) const


template<typename Vector>
constexpr MatrixInformation Parser::parseToMatrixRowMajor(std::string_view data, Vector& values) const
constexpr MatrixInformation Parser::parseToVectorRowMajor(std::string_view data, Vector& values) const
{
return parseToMatrixRowMajor(data, values, std::nullopt, std::nullopt);
return parseToVectorRowMajor(data, values, std::nullopt, std::nullopt);
}



template<typename Vector>
constexpr MatrixInformation Parser::parseToMatrixRowMajor(std::string_view data, Vector& values,
constexpr MatrixInformation Parser::parseToVectorRowMajor(std::string_view data, Vector& values,
std::optional<std::size_t> rows_hint, std::optional<std::size_t> columns) const
{
Vector parsed_values;
Expand Down Expand Up @@ -556,8 +570,8 @@ constexpr MatrixInformation Parser::parseToMatrixRowMajor(std::string_view data,
parsed_values.resize((row + 1) * info.getColumns());
}
}
using Number = typename Vector::value_type;
parsed_values[info.matrixIndex(row, column)] = readNumber<Number>(cell_data).value_or(std::numeric_limits<Number>::quiet_NaN());
parsed_values[info.matrixIndex(row, column)] =
CellTrait<typename Vector::value_type>::create(cell_data, hint);
}
);
std::swap(values, parsed_values);
Expand All @@ -570,7 +584,8 @@ constexpr MatrixInformation Parser::parseToMatrixRowMajor(std::string_view data,


template<typename Vector>
constexpr MatrixInformation Parser::parseToMatrixColumnMajor(std::string_view data, Vector& values, std::size_t rows, std::optional<std::size_t> columns_hint) const
constexpr MatrixInformation Parser::parseToVectorColumnMajor(std::string_view data, Vector& values,
std::size_t rows, std::optional<std::size_t> columns_hint) const
{
Vector parsed_values;
if (columns_hint.has_value()) {
Expand All @@ -587,8 +602,8 @@ constexpr MatrixInformation Parser::parseToMatrixColumnMajor(std::string_view da
if (parsed_values.size() < ((column + 1) * rows)) {
parsed_values.resize((column + 1) * rows);
}
using Number = typename Vector::value_type;
parsed_values[info.matrixIndex(row, column)] = readNumber<Number>(cell_data).value_or(std::numeric_limits<Number>::quiet_NaN());
parsed_values[info.matrixIndex(row, column)] =
CellTrait<typename Vector::value_type>::create(cell_data, hint);
}
);
std::swap(values, parsed_values);
Expand All @@ -600,6 +615,26 @@ constexpr MatrixInformation Parser::parseToMatrixColumnMajor(std::string_view da



template<std::size_t rows, std::size_t columns, typename Cell>
constexpr auto Parser::parseToArray(std::string_view data, MatrixOrder order) const
{
std::array<Cell, rows * columns> matrix;

parse(data,
[&matrix, order](std::size_t row, std::size_t column,
std::string_view cell_data, Csv::CellTypeHint hint)
constexpr
{
matrix[MatrixInformation::matrixIndex(row, column, rows, columns, order)] =
CellTrait<Cell>::create(cell_data, hint);
}
);

return matrix;
}



template<typename Vector2D>
constexpr auto Parser::vector2DValue(const Vector2D& values, std::size_t row, std::size_t column)
{
Expand Down
8 changes: 4 additions & 4 deletions examples/example_runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ int parsePredefinedData()
Csv::Parser parser;

try {
// parseTo() throws ParseError on error.
parser.parseTo(data, cell_refs);
// This throws ParseError on error.
parser.parseTo2DVector(data, cell_refs);
}
catch(Csv::ParseError& ex) {
std::cerr << "CSV parse error: " << ex.what() << std::endl;
Expand Down Expand Up @@ -83,8 +83,8 @@ int main(int argc, char** argv)
try {
Csv::Parser parser;

// parseTo() throws ParseError on error.
parser.parseTo(csv_data, cell_refs);
// This throws ParseError on error.
parser.parseTo2DVector(csv_data, cell_refs);
}
catch(Csv::ParseError& ex) {
std::cerr << "CSV parse error: " << ex.what() << std::endl;
Expand Down
Loading

0 comments on commit dd00589

Please sign in to comment.