Skip to content

Commit 8f4cf06

Browse files
committed
Csv::Parser now accepts behavior policy as a template argument; the policy is also passed to Cell classes so that they can use it, replacing the previous CellTrait facility.
The new behavior policy mechanism allows user-defined number parsing functions, making the parser pluggable and allowing libraries like fast_float to be used. Two policies are implemented - locale-aware (previous behavior, default), and locale-unaware (using std::from_chars() with newer compilers). Made more methods constexpr; integer parsing can be done at compile-time now (when using C++23+), and float/double compile-time parsing can be done using fast_float if needed. Split into more headers.
1 parent 9b6d0e0 commit 8f4cf06

File tree

13 files changed

+758
-264
lines changed

13 files changed

+758
-264
lines changed

CMakeLists.txt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ project(csv-parser
55
LANGUAGES CXX
66
)
77

8-
set(CMAKE_CXX_STANDARD 17)
9-
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
8+
# Set the C++ standard to C++23 if available.
9+
# Generally, we require at least C++17, but C++23 is preferred.
10+
set(CMAKE_CXX_STANDARD 23)
11+
set(CMAKE_CXX_STANDARD_REQUIRED FALSE)
1012

1113

1214
add_library(compiler_warnings INTERFACE)
@@ -25,6 +27,7 @@ target_sources(csv_parser
2527
${CMAKE_CURRENT_SOURCE_DIR}/csv_parser/csv_error.h
2628
${CMAKE_CURRENT_SOURCE_DIR}/csv_parser/csv_matrix.h
2729
${CMAKE_CURRENT_SOURCE_DIR}/csv_parser/csv_parser.h
30+
${CMAKE_CURRENT_SOURCE_DIR}/csv_parser/csv_policies.h
2831
${CMAKE_CURRENT_SOURCE_DIR}/csv_parser/csv_util.h
2932
)
3033
target_include_directories(csv_parser

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2021 Alexander Shaduri <[email protected]>
1+
Copyright (c) 2021 - 2025 Alexander Shaduri <[email protected]>
22

33
This software is provided 'as-is', without any express or implied
44
warranty. In no event will the authors be held liable for any damages

README.md

Lines changed: 110 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
# Csv::Parser (csv-parser)
2-
***Compile-time and runtime CSV parser written in C++17***
2+
***Compile-time and runtime CSV parser written in Modern C++***
33

44
[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/ashaduri/csv-parser?label=Version)](https://github.com/ashaduri/csv-parser)
55
![GitHub](https://img.shields.io/github/license/ashaduri/csv-parser)
6-
![Language](https://img.shields.io/badge/language-ISO%20C++17-blue)
6+
![Language](https://img.shields.io/badge/language-Modern%20C++-blue)
77

88

99
## Features
1010
- Header-only.
11-
- Requires only standard C++ (C++17).
12-
- Supports reading CSV data from `std::string_view` during compilation (using `constexpr`).
11+
- Requires only standard C++ (minimum C++17, with C++23 support providing additional functionality).
12+
- Supports reading CSV data from `std::string_view` at compile-time.
1313
- Fully supports [RFC 4180](https://www.ietf.org/rfc/rfc4180.txt), including quoted values, escaped quotes, and newlines in field values.
1414
- Liberal in terms of accepting not-quite-standard CSV files, but detects errors when needed.
1515
- Supports Excel CSV variations.
16-
- Supports reading data as different types (string, double, empty field) (runtime only).
16+
- Supports reading data as different types (string, double, empty field), with some restrictions when parsing at
17+
compile-time.
18+
- Modular design allows for easy extension of the library.
1719
- Extensively tested using [Catch2](https://github.com/catchorg/Catch2).
1820

1921
## API Reference
@@ -114,11 +116,14 @@ std::cout << "Row 1, column 2: " << matrix_data[info.matrixIndex(1, 2)] << std::
114116
return EXIT_SUCCESS;
115117
```
116118

117-
### Compile-Time Parsing
119+
### Compile-Time Parsing Into 2D `std::array`
118120

119121
Currently, parsing at compile-time has some restrictions:
120-
- Only string_views are supported for output (no doubles).
121122
- To collapse consecutive double-quotes in strings, a compile-time-allocated buffer has to be used.
123+
- By default, only string_views are supported for output (no numeric types).
124+
- If using C++23 or later, integral types are supported as well.
125+
- If compile-time parsing of floating point numbers is needed,
126+
[fast_float](https://github.com/fastfloat/fast_float) can be plugged in.
122127

123128
One (possibly useful) consequence of compile-time parsing is that a parse error also causes a compilation error.
124129

@@ -130,12 +135,11 @@ One (possibly useful) consequence of compile-time parsing is that a parse error
130135

131136
using namespace std::string_view_literals;
132137

138+
constexpr std::size_t columns = 2, rows = 2;
133139
constexpr std::string_view data =
134140
R"(abc, "def"
135141
"with ""quote inside",6)";
136142

137-
constexpr std::size_t columns = 2, rows = 2;
138-
139143
constexpr Csv::Parser parser;
140144

141145
// parse into std::array<std::array<CellStringReference, rows>, columns>
@@ -158,6 +162,103 @@ constexpr auto buffer = matrix[0][1].getCleanStringBuffer<buffer_size>();
158162
static_assert(buffer.getStringView() == R"(with "quote inside)"sv);
159163
```
160164
165+
### Compile-Time Parsing of Integral Matrix Into 1D Vector
166+
167+
The library can be used to parse CSV data at compile-time into a 1D vector, in row-major
168+
or column-major order.
169+
Additionally, compile-time parsing of integral types is supported since C++23.
170+
171+
#### Example:
172+
```cpp
173+
#include "csv_parser.h"
174+
175+
// ...
176+
177+
using namespace std::string_view_literals;
178+
179+
constexpr std::size_t columns = 2, rows = 3;
180+
constexpr std::string_view data =
181+
R"(11, -12
182+
21, 4
183+
60, -10)";
184+
185+
// Use Csv::LocaleUnawareBehaviorPolicy to avoid locale issues and allow compile-time integer parsing.
186+
constexpr Csv::Parser<Csv::LocaleUnawareBehaviorPolicy> parser;
187+
188+
// Parse into std::array<CellStringReference, rows * columns> in row-major order
189+
{
190+
constexpr auto matrix = parser.parseToArray<rows, columns>(data, Csv::MatrixOrder::RowMajor);
191+
192+
static_assert(matrix[0].getOriginalStringView() == "11"sv);
193+
static_assert(matrix[2].getOriginalStringView() == "21"sv);
194+
}
195+
196+
// Parse into std::array<std::int64_t, rows * columns> in column-major order.
197+
// Compile-time parsing to integers is supported since C++23.
198+
#if __cplusplus >= 202300L
199+
{
200+
constexpr auto matrix = parser.parseToArray<rows, columns, std::int64_t>(data, Csv::MatrixOrder::ColumnMajor);
201+
202+
static_assert(matrix[0] == 11);
203+
static_assert(matrix[2] == 60);
204+
}
205+
#endif
206+
```
207+
208+
209+
### Compile-Time Parsing of Numeric Matrix Using External Library for Floating Point Numbers
210+
211+
A library like [fast_float](https://github.com/fastfloat/fast_float) can be used to parse floating point
212+
numbers at compile-time. This is done by creating a custom policy that implements the `readNumber` and
213+
`create` methods.
214+
This approach also allows for improving the performance of parsing floating point numbers compared
215+
to the standard library.
216+
217+
#### Example:
218+
```cpp
219+
#include "csv_parser.h"
220+
#include "fast_float/fast_float.h"
221+
222+
// Custom policy which uses fast_float library for parsing to floating point.
223+
struct CustomPolicy : Csv::LocaleUnawareBehaviorPolicy {
224+
template<typename Number>
225+
static constexpr std::optional<Number> readNumber(std::string_view cell)
226+
{
227+
Number parsed_value = 0;
228+
auto [ptr, ec] = fast_float::from_chars(cell.begin(), cell.end(), parsed_value);
229+
if (ec == std::errc() && ptr == (cell.end())) {
230+
return parsed_value;
231+
}
232+
return std::nullopt;
233+
}
234+
235+
template<typename CellT>
236+
static constexpr CellT create(std::string_view cell, Csv::CellTypeHint hint)
237+
{
238+
return readNumber<CellT>(cell).value_or(std::numeric_limits<CellT>::quiet_NaN());
239+
}
240+
};
241+
242+
243+
void parse()
244+
{
245+
constexpr std::string_view data =
246+
R"(11.6, -12.3
247+
2e5, -inf
248+
nan, inf)";
249+
250+
// Use custom policy which uses fast_float library for parsing to floating point.
251+
constexpr Csv::Parser<CustomPolicy> parser;
252+
constexpr std::size_t columns = 2, rows = 3;
253+
254+
// Parse into std::array<double, rows * columns> in row-major order
255+
constexpr auto matrix = parser.parseToArray<rows, columns, double>(data, Csv::MatrixOrder::RowMajor);
256+
257+
static_assert(matrix[0] == 11.6);
258+
static_assert(matrix[3] == -std::numeric_limits<double>::infinity());
259+
}
260+
```
261+
161262

162263
## Copyright
163264

0 commit comments

Comments
 (0)