Skip to content

Commit 95e304b

Browse files
committed
Clean up CSVField duplication
1 parent f288155 commit 95e304b

5 files changed

Lines changed: 9550 additions & 105 deletions

File tree

include/internal/basic_csv_parser_guessing.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#include "basic_csv_parser.hpp"
2+
#include "string_view_stream.hpp"
23

3-
#include <sstream>
44
#include <unordered_map>
55

66
namespace csv {
@@ -15,14 +15,14 @@ namespace csv {
1515
// Parse the CSV using the low-level constructor that takes pre-built flag
1616
// tables — bypasses format resolution entirely and avoids recursion back
1717
// into guess_format.
18-
std::stringstream source(head.data());
18+
internals::StringViewStream source(head);
1919
RowCollection rows;
2020

2121
const auto parse_flags = format.is_quoting_enabled()
2222
? internals::make_parse_flags(format.get_delim(), format.get_quote_char())
2323
: internals::make_parse_flags(format.get_delim());
2424
const auto ws_flags = internals::make_ws_flags(format.get_trim_chars());
25-
StreamParser<std::stringstream> parser(source, parse_flags, ws_flags);
25+
StreamParser<internals::StringViewStream> parser(source, parse_flags, ws_flags);
2626
parser.set_output(rows);
2727
parser.next();
2828

include/internal/csv_row.hpp

Lines changed: 43 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -102,66 +102,15 @@ namespace csv {
102102
*
103103
*/
104104
template<typename T = std::string> T get() {
105-
IF_CONSTEXPR(std::is_arithmetic<T>::value) {
106-
// Note: this->type() also converts the CSV value to float
107-
if (this->type() <= DataType::CSV_STRING) {
108-
throw std::runtime_error(internals::ERROR_NAN);
109-
}
110-
}
111-
112-
IF_CONSTEXPR(std::is_integral<T>::value) {
113-
// Note: this->is_float() also converts the CSV value to float
114-
if (this->is_float()) {
115-
throw std::runtime_error(internals::ERROR_FLOAT_TO_INT);
116-
}
117-
118-
IF_CONSTEXPR(std::is_unsigned<T>::value) {
119-
if (this->value < 0) {
120-
throw std::runtime_error(internals::ERROR_NEG_TO_UNSIGNED);
121-
}
122-
}
123-
}
124-
125-
// Allow fallthrough from previous if branch
126-
IF_CONSTEXPR(!std::is_floating_point<T>::value) {
127-
IF_CONSTEXPR(std::is_unsigned<T>::value) {
128-
// Quick hack to perform correct unsigned integer boundary checks
129-
if (this->value > internals::get_uint_max<sizeof(T)>()) {
130-
throw std::runtime_error(internals::ERROR_OVERFLOW);
131-
}
132-
}
133-
else if (internals::type_num<T>() < this->_type) {
134-
throw std::runtime_error(internals::ERROR_OVERFLOW);
135-
}
136-
}
137-
138-
return static_cast<T>(this->value);
105+
T out{};
106+
if (const auto* err = check_convert(out)) throw std::runtime_error(err);
107+
return out;
139108
}
140109

141-
/** Attempts to retrieve the value as the requested type without throwing exceptions.
142-
*
143-
* @param[out] out Output parameter that receives the converted value if successful
144-
* @return true if conversion succeeded, false otherwise
145-
*
146-
* \par Valid options for T
147-
* - std::string or csv::string_view
148-
* - signed integral types (signed char, short, int, long int, long long int)
149-
* - floating point types (float, double, long double)
150-
* - unsigned integers are not supported at this time, but may be in a later release
151-
*
152-
* \par When conversion fails (returns false)
153-
* - Converting non-numeric values to any numeric type
154-
* - Converting floating point values to integers
155-
* - Converting a large integer to a smaller type that will not hold it
156-
* - Converting negative values to unsigned types
110+
/** Non-throwing equivalent of get(). Applies the same type checks and conversions;
111+
* returns true and writes to @p out on success, or returns false without throwing.
157112
*
158-
* @note This method is capable of parsing scientific E-notation.
159-
*
160-
* @warning Currently, conversions to floating point types are not
161-
* checked for loss of precision
162-
*
163-
* @warning Any string_views returned are only guaranteed to be valid
164-
* if the parent CSVRow is still alive.
113+
* @sa get() for the full description of valid types, conversion rules, and warnings.
165114
*
166115
* Example:
167116
* @code
@@ -175,40 +124,7 @@ namespace csv {
175124
*/
176125
template<typename T = std::string>
177126
bool try_get(T& out) noexcept {
178-
IF_CONSTEXPR(std::is_arithmetic<T>::value) {
179-
// Check if value is numeric
180-
if (this->type() <= DataType::CSV_STRING) {
181-
return false;
182-
}
183-
}
184-
185-
IF_CONSTEXPR(std::is_integral<T>::value) {
186-
// Check for float-to-int conversion
187-
if (this->is_float()) {
188-
return false;
189-
}
190-
191-
IF_CONSTEXPR(std::is_unsigned<T>::value) {
192-
if (this->value < 0) {
193-
return false;
194-
}
195-
}
196-
}
197-
198-
// Check for overflow
199-
IF_CONSTEXPR(!std::is_floating_point<T>::value) {
200-
IF_CONSTEXPR(std::is_unsigned<T>::value) {
201-
if (this->value > internals::get_uint_max<sizeof(T)>()) {
202-
return false;
203-
}
204-
}
205-
else if (internals::type_num<T>() < this->_type) {
206-
return false;
207-
}
208-
}
209-
210-
out = static_cast<T>(this->value);
211-
return true;
127+
return check_convert(out) == nullptr;
212128
}
213129

214130
/** Parse a hexadecimal value, returning false if the value is not hex.
@@ -294,6 +210,42 @@ namespace csv {
294210
long double value = 0; /**< Cached numeric value */
295211
csv::string_view sv = ""; /**< A pointer to this field's text */
296212
DataType _type = DataType::UNKNOWN; /**< Cached data type value */
213+
214+
/** Shared validation + conversion kernel used by get() and try_get().
215+
* Assigns to @p out and returns nullptr on success;
216+
* returns a pointer to a static error message on failure, without throwing.
217+
*/
218+
template<typename T>
219+
const char* check_convert(T& out) noexcept {
220+
IF_CONSTEXPR(std::is_arithmetic<T>::value) {
221+
if (this->type() <= DataType::CSV_STRING)
222+
return internals::ERROR_NAN.c_str();
223+
}
224+
225+
IF_CONSTEXPR(std::is_integral<T>::value) {
226+
if (this->is_float())
227+
return internals::ERROR_FLOAT_TO_INT.c_str();
228+
229+
IF_CONSTEXPR(std::is_unsigned<T>::value) {
230+
if (this->value < 0)
231+
return internals::ERROR_NEG_TO_UNSIGNED.c_str();
232+
}
233+
}
234+
235+
IF_CONSTEXPR(!std::is_floating_point<T>::value) {
236+
IF_CONSTEXPR(std::is_unsigned<T>::value) {
237+
if (this->value > internals::get_uint_max<sizeof(T)>())
238+
return internals::ERROR_OVERFLOW.c_str();
239+
}
240+
else if (internals::type_num<T>() < this->_type) {
241+
return internals::ERROR_OVERFLOW.c_str();
242+
}
243+
}
244+
245+
out = static_cast<T>(this->value);
246+
return nullptr;
247+
}
248+
297249
CONSTEXPR_14 void get_value() noexcept {
298250
/* Check to see if value has been cached previously, if not
299251
* evaluate it

include/internal/csv_writer.hpp

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -382,28 +382,24 @@ namespace csv {
382382
, int> = 0
383383
>
384384
std::string csv_escape(T in) {
385-
IF_CONSTEXPR(std::is_convertible<T, csv::string_view>::value) {
385+
IF_CONSTEXPR(std::is_convertible<T, csv::string_view>::value)
386386
return _csv_escape(in);
387-
}
388-
else {
389-
return _csv_escape(std::string(in));
390-
}
387+
388+
return _csv_escape(std::string(in));
391389
}
392390

393391
std::string _csv_escape(csv::string_view in) {
394392
// Format a string to be RFC 4180-compliant.
395-
396-
// Do we need a quote escape
397-
bool quote_escape = false;
393+
bool quote_escape_needed = false;
398394

399395
for (auto ch : in) {
400396
if (ch == Quote || ch == Delim || ch == '\r' || ch == '\n') {
401-
quote_escape = true;
397+
quote_escape_needed = true;
402398
break;
403399
}
404400
}
405401

406-
if (!quote_escape) {
402+
if (!quote_escape_needed) {
407403
if (quote_minimal) return std::string(in);
408404
else {
409405
std::string ret(1, Quote);

0 commit comments

Comments
 (0)