mirror of
https://github.com/red0124/ssp.git
synced 2025-12-16 06:39:55 +01:00
Compare commits
18 Commits
110ee840cc
...
v1.7.0
| Author | SHA1 | Date | |
|---|---|---|---|
| ddaa446819 | |||
|
|
8bad2d72ea | ||
| 899a6e6f5e | |||
| 0d3d8fa83e | |||
| 7bbe2879cd | |||
| 063d56fad9 | |||
| df78865f04 | |||
| 852481d233 | |||
| c516a6f826 | |||
| b660310acf | |||
| 0a695cf09e | |||
| f8e14b1fcf | |||
| 0ebbee1174 | |||
| b3f3bdf8d1 | |||
| f4a06d40e7 | |||
| f2ff40a625 | |||
|
|
a27fd121a1 | ||
| c0d3087f85 |
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.14)
|
|||||||
|
|
||||||
project(
|
project(
|
||||||
ssp
|
ssp
|
||||||
VERSION 1.6.2
|
VERSION 1.7.0
|
||||||
DESCRIPTION "csv parser"
|
DESCRIPTION "csv parser"
|
||||||
HOMEPAGE_URL "https://github.com/red0124/ssp"
|
HOMEPAGE_URL "https://github.com/red0124/ssp"
|
||||||
LANGUAGES CXX
|
LANGUAGES CXX
|
||||||
|
|||||||
49
README.md
49
README.md
@@ -17,13 +17,13 @@
|
|||||||
[](https://github.com/red0124/ssp/actions/workflows/win-msys2-clang.yml)
|
[](https://github.com/red0124/ssp/actions/workflows/win-msys2-clang.yml)
|
||||||
[](https://github.com/red0124/ssp/actions/workflows/win-msvc.yml)
|
[](https://github.com/red0124/ssp/actions/workflows/win-msvc.yml)
|
||||||
|
|
||||||
A header only "csv" parser which is fast and versatile with modern C++ api. Requires compiler with C++17 support. [Can also be used to convert strings to specific types.](#the-converter)
|
A header only CSV parser which is fast and versatile with modern C++ API. Requires compiler with C++17 support. [Can also be used to efficiently convert strings to specific types.](#the-converter)
|
||||||
|
|
||||||
Conversion for floating point values invoked using [fast-float](https://github.com/fastfloat/fast_float) . \
|
Conversion for floating point values invoked using [fast-float](https://github.com/fastfloat/fast_float) . \
|
||||||
Function traits taken from *qt-creator* .
|
Function traits taken from *qt-creator* .
|
||||||
|
|
||||||
# Example
|
# Example
|
||||||
Lets say we have a csv file containing students in a given format \<Id,Age,Grade\> and we want to parse and print all the valid values:
|
Lets say we have a CSV file containing students in a given format (Id,Age,Grade) and we want to parse and print all the valid values:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ cat students.csv
|
$ cat students.csv
|
||||||
@@ -58,6 +58,7 @@ Bill (Heath) Gates 65 3.3
|
|||||||
* Can work without exceptions
|
* Can work without exceptions
|
||||||
* [Works with headers](#headers)
|
* [Works with headers](#headers)
|
||||||
* [Works with quotes, escapes and spacings](#setup)
|
* [Works with quotes, escapes and spacings](#setup)
|
||||||
|
* [Works with CSV data stored in buffers](#buffer-mode)
|
||||||
* [Works with values containing new lines](#multiline)
|
* [Works with values containing new lines](#multiline)
|
||||||
* [Columns and rows can be ignored](#special-types)
|
* [Columns and rows can be ignored](#special-types)
|
||||||
* [Works with any type of delimiter](#delimiter)
|
* [Works with any type of delimiter](#delimiter)
|
||||||
@@ -158,7 +159,7 @@ while (!p.eof()) {
|
|||||||
|
|
||||||
The alternate example with exceptions disabled will be used to show some of the features of the library. The **`get_next`** method returns a tuple of objects specified inside the template type list.
|
The alternate example with exceptions disabled will be used to show some of the features of the library. The **`get_next`** method returns a tuple of objects specified inside the template type list.
|
||||||
|
|
||||||
If a conversion could not be applied, the method would return a tuple of default constructed objects, and the **`valid`** method would return **`false`**, for example if the third (grade) column in our csv could not be converted to a float the conversion would fail.
|
If a conversion could not be applied, the method would return a tuple of default constructed objects, and the **`valid`** method would return **`false`**, for example if the third (grade) column in our CSV could not be converted to a float the conversion would fail.
|
||||||
|
|
||||||
If **`get_next`** is called with a **`tuple`** as template parameter it would behave identically to passing the same tuple parameters to **`get_next`**:
|
If **`get_next`** is called with a **`tuple`** as template parameter it would behave identically to passing the same tuple parameters to **`get_next`**:
|
||||||
```cpp
|
```cpp
|
||||||
@@ -202,14 +203,27 @@ struct student {
|
|||||||
auto tied() { return std::tie(id, age, grade); }
|
auto tied() { return std::tie(id, age, grade); }
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
The method can be used to compare the object, serialize it, deserialize it, etc. Now **`get_next`** can accept such a struct and deduce the types to which to convert the csv.
|
The method can be used to compare the object, serialize it, deserialize it, etc. Now **`get_next`** can accept such a struct and deduce the types to which to convert the CSV.
|
||||||
```cpp
|
```cpp
|
||||||
// returns student
|
// returns student
|
||||||
auto s = p.get_next<student>();
|
auto s = p.get_next<student>();
|
||||||
```
|
```
|
||||||
This works with the iteration loop too.
|
This works with the iteration loop too.
|
||||||
*Note, the order in which the members of the tied method are returned must match the order of the elements in the csv*.
|
*Note, the order in which the members of the tied method are returned must match the order of the elements in the CSV*.
|
||||||
|
|
||||||
|
## Buffer mode
|
||||||
|
The parser also works with buffers containing CSV data instead of files. To parse buffer data with the parser simply create the parser by giving it the buffer, as **`const char*`**, and its size. The initial example using a buffer instead of a file would look similar to this:
|
||||||
|
```cpp
|
||||||
|
std::string buffer = "James Bailey,65,2.5\nBrian S. Wolfe,40,1.9\n";
|
||||||
|
|
||||||
|
ss::parser<ss::throw_on_error> p{buffer.c_str(), buffer.size()};
|
||||||
|
|
||||||
|
for (const auto& [id, age, grade] : p.iterate<std::string, int, float>()) {
|
||||||
|
std::cout << id << ' ' << age << ' ' << grade << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
```
|
||||||
## Setup
|
## Setup
|
||||||
By default, many of the features supported by the parser are disabled. They can be enabled within the template parameters of the parser. For example, to enable quoting and escaping the parser would look like:
|
By default, many of the features supported by the parser are disabled. They can be enabled within the template parameters of the parser. For example, to enable quoting and escaping the parser would look like:
|
||||||
```cpp
|
```cpp
|
||||||
@@ -241,7 +255,7 @@ Empty lines can be ignored by defining **`ss::ignore_empty`** within the setup p
|
|||||||
```cpp
|
```cpp
|
||||||
ss::parser<ss::ignore_empty> p{file_name};
|
ss::parser<ss::ignore_empty> p{file_name};
|
||||||
```
|
```
|
||||||
If this setup option is not set then reading an empty line will result in an error (unless only one column is present within the csv).
|
If this setup option is not set then reading an empty line will result in an error (unless only one column is present within the CSV).
|
||||||
|
|
||||||
### Quoting
|
### Quoting
|
||||||
Quoting can be enabled by defining **`ss::quote`** within the setup parameters. A single character can be defined as the quoting character, for example to use **`"`** as a quoting character:
|
Quoting can be enabled by defining **`ss::quote`** within the setup parameters. A single character can be defined as the quoting character, for example to use **`"`** as a quoting character:
|
||||||
@@ -290,7 +304,7 @@ Escaping and quoting can be used to leave the space if needed.
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Multiline
|
### Multiline
|
||||||
Multiline can be enabled by defining **`ss::multilne`** within the setup parameters. It enables the possibility to have the new line characters within rows. The new line character needs to be either escaped or within quotes so either **`ss::escape`** or **`ss::quote`** need to be enabled. There is a specific problem when using multiline, for example, if a row had an unterminated quote, the parser would assume it to be a new line within the row, so until another quote is found, it will treat it as one line which is fine usually, but it can cause the whole csv file to be treated as a single line by mistake. To prevent this **`ss::multiline_restricted`** can be used which accepts an unsigned number representing the maximum number of lines which can be allowed as a single multiline. Examples:
|
Multiline can be enabled by defining **`ss::multilne`** within the setup parameters. It enables the possibility to have the new line characters within rows. The new line character needs to be either escaped or within quotes so either **`ss::escape`** or **`ss::quote`** need to be enabled. There is a specific problem when using multiline, for example, if a row had an unterminated quote, the parser would assume it to be a new line within the row, so until another quote is found, it will treat it as one line which is fine usually, but it can cause the whole CSV file to be treated as a single line by mistake. To prevent this **`ss::multiline_restricted`** can be used which accepts an unsigned number representing the maximum number of lines which can be allowed as a single multiline. Examples:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
ss::parser<ss::multiline, ss::quote<'\"'>, ss::escape<'\\'>> p{file_name};
|
ss::parser<ss::multiline, ss::quote<'\"'>, ss::escape<'\\'>> p{file_name};
|
||||||
@@ -341,7 +355,7 @@ Gates 65 3.3'
|
|||||||
```
|
```
|
||||||
## Special types
|
## Special types
|
||||||
|
|
||||||
Passing **`void`** makes the parser ignore a column. In the initial example **`void`** could be given as the second template parameter to ignore the second (age) column in the csv, a tuple of only 2 parameters would be retuned:
|
Passing **`void`** makes the parser ignore a column. In the initial example **`void`** could be given as the second template parameter to ignore the second (age) column in the CSV, a tuple of only 2 parameters would be retuned:
|
||||||
```cpp
|
```cpp
|
||||||
// returns std::tuple<std::string, float>
|
// returns std::tuple<std::string, float>
|
||||||
auto [id, grade] = p.get_next<std::string, void, float>();
|
auto [id, grade] = p.get_next<std::string, void, float>();
|
||||||
@@ -383,6 +397,12 @@ if (std::holds_alternative<float>(grade)) {
|
|||||||
// grade set as char
|
// grade set as char
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
Passing **`char`** and types that are aliases to it such as **`uint8_t`** and **`int8_t`** make the parser interpret the input data as a single character in a similar way to how **`std::cin`** does it. To read numeric values into something like **`uint8_t`** the **`ss::uint8`** and **`ss::int8`** types can be used. These are wrappers arround the corresponding char aliases and can be implicitly converted to and from them. When these types are given to the parser he will try to read the given data and store it in the underlying element, but this time as a numeric value instead of a single character.
|
||||||
|
```cpp
|
||||||
|
// returns std::tuple<std::string, ss::uint8, float>
|
||||||
|
auto [id, age, grade] = p.get_next<std::string, ss::uint8, float>();
|
||||||
|
uint8_t age_copy = age;
|
||||||
|
```
|
||||||
## Restrictions
|
## Restrictions
|
||||||
|
|
||||||
Custom **`restrictions`** can be used to narrow down the conversions of unwanted values. **`ss::ir`** (in range) and **`ss::ne`** (none empty) are some of those:
|
Custom **`restrictions`** can be used to narrow down the conversions of unwanted values. **`ss::ir`** (in range) and **`ss::ne`** (none empty) are some of those:
|
||||||
@@ -454,12 +474,13 @@ The **`eof`** method can be used to detect if the end of the file was reached.
|
|||||||
Detailed error messages can be accessed via the **`error_msg`** method, and to enable them **`ss::string_error`** needs to be included in the setup. If **`ss::string_error`** is not defined, the **`error_msg`** method will not be defined either.
|
Detailed error messages can be accessed via the **`error_msg`** method, and to enable them **`ss::string_error`** needs to be included in the setup. If **`ss::string_error`** is not defined, the **`error_msg`** method will not be defined either.
|
||||||
|
|
||||||
The line number can be fetched using the **`line`** method.
|
The line number can be fetched using the **`line`** method.
|
||||||
|
The cursor position can be fetched using the **`position`** method.
|
||||||
```cpp
|
```cpp
|
||||||
const std::string& parser::error_msg();
|
const std::string& parser::error_msg() const;
|
||||||
bool parser::valid();
|
bool parser::valid() const;
|
||||||
bool parser::eof();
|
bool parser::eof() const;
|
||||||
size_t parser::line();
|
size_t parser::line() const;
|
||||||
|
size_t parser::position() const;
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
ss::parser<ss::string_error> parser;
|
ss::parser<ss::string_error> parser;
|
||||||
@@ -474,7 +495,7 @@ ss::parser<ss::throw_on_error> parser;
|
|||||||
|
|
||||||
## Substitute conversions
|
## Substitute conversions
|
||||||
|
|
||||||
The parser can also be used to effectively parse files whose rows are not always in the same format (not a classical csv but still csv-like). A more complicated example would be the best way to demonstrate such a scenario.\
|
The parser can also be used to effectively parse files whose rows are not always in the same format (not a classical CSV but still CSV-like). A more complicated example would be the best way to demonstrate such a scenario.\
|
||||||
***Important, substitute conversions do not work when throw_on_error is enabled.***
|
***Important, substitute conversions do not work when throw_on_error is enabled.***
|
||||||
|
|
||||||
Supposing we have a file containing different shapes in given formats:
|
Supposing we have a file containing different shapes in given formats:
|
||||||
|
|||||||
@@ -28,6 +28,15 @@ inline void assert_throw_on_error_not_defined() {
|
|||||||
"'throw_on_error' is enabled");
|
"'throw_on_error' is enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void* strict_realloc(void* ptr, size_t size) {
|
||||||
|
ptr = std::realloc(ptr, size);
|
||||||
|
if (!ptr) {
|
||||||
|
throw std::bad_alloc{};
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
#if __unix__
|
#if __unix__
|
||||||
inline ssize_t get_line_file(char** lineptr, size_t* n, FILE* stream) {
|
inline ssize_t get_line_file(char** lineptr, size_t* n, FILE* stream) {
|
||||||
return getline(lineptr, n, stream);
|
return getline(lineptr, n, stream);
|
||||||
@@ -46,37 +55,24 @@ ssize_t get_line_file(char** lineptr, size_t* n, FILE* fp) {
|
|||||||
|
|
||||||
if (*lineptr == nullptr || *n < sizeof(buff)) {
|
if (*lineptr == nullptr || *n < sizeof(buff)) {
|
||||||
size_t new_n = sizeof(buff);
|
size_t new_n = sizeof(buff);
|
||||||
auto new_lineptr = static_cast<char*>(realloc(*lineptr, new_n));
|
*lineptr = static_cast<char*>(strict_realloc(*lineptr, new_n));
|
||||||
if (new_lineptr == nullptr) {
|
|
||||||
errno = ENOMEM;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
*lineptr = new_lineptr;
|
|
||||||
*n = new_n;
|
*n = new_n;
|
||||||
}
|
}
|
||||||
|
|
||||||
(*lineptr)[0] = '\0';
|
(*lineptr)[0] = '\0';
|
||||||
|
|
||||||
size_t line_used = 0;
|
size_t line_used = 0;
|
||||||
while (fgets(buff, sizeof(buff), fp) != nullptr) {
|
while (std::fgets(buff, sizeof(buff), fp) != nullptr) {
|
||||||
line_used = strlen(*lineptr);
|
line_used = std::strlen(*lineptr);
|
||||||
size_t buff_used = strlen(buff);
|
size_t buff_used = std::strlen(buff);
|
||||||
|
|
||||||
if (*n <= buff_used + line_used) {
|
if (*n <= buff_used + line_used) {
|
||||||
size_t new_n = *n * 2;
|
size_t new_n = *n * 2;
|
||||||
|
*lineptr = static_cast<char*>(strict_realloc(*lineptr, new_n));
|
||||||
auto new_lineptr = static_cast<char*>(realloc(*lineptr, new_n));
|
|
||||||
if (new_lineptr == nullptr) {
|
|
||||||
errno = ENOMEM;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
*lineptr = new_lineptr;
|
|
||||||
*n = new_n;
|
*n = new_n;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(*lineptr + line_used, buff, buff_used);
|
std::memcpy(*lineptr + line_used, buff, buff_used);
|
||||||
line_used += buff_used;
|
line_used += buff_used;
|
||||||
(*lineptr)[line_used] = '\0';
|
(*lineptr)[line_used] = '\0';
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,38 @@ std::enable_if_t<std::is_floating_point_v<T>, std::optional<T>> to_num(
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
////////////////
|
||||||
|
// numeric_wrapper
|
||||||
|
////////////////
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct numeric_wrapper {
|
||||||
|
using type = T;
|
||||||
|
|
||||||
|
numeric_wrapper() = default;
|
||||||
|
numeric_wrapper(numeric_wrapper&&) = default;
|
||||||
|
numeric_wrapper(const numeric_wrapper&) = default;
|
||||||
|
|
||||||
|
numeric_wrapper& operator=(numeric_wrapper&&) = default;
|
||||||
|
numeric_wrapper& operator=(const numeric_wrapper&) = default;
|
||||||
|
|
||||||
|
numeric_wrapper(T other) : value{other} {
|
||||||
|
}
|
||||||
|
|
||||||
|
operator T() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator T() const {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
T value;
|
||||||
|
};
|
||||||
|
|
||||||
|
using int8 = numeric_wrapper<int8_t>;
|
||||||
|
using uint8 = numeric_wrapper<uint8_t>;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::enable_if_t<std::is_integral_v<T>, std::optional<T>> to_num(
|
std::enable_if_t<std::is_integral_v<T>, std::optional<T>> to_num(
|
||||||
const char* const begin, const char* const end) {
|
const char* const begin, const char* const end) {
|
||||||
@@ -89,6 +121,18 @@ std::enable_if_t<std::is_integral_v<T>, std::optional<T>> to_num(
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::enable_if_t<is_instance_of_v<numeric_wrapper, T>, std::optional<T>> to_num(
|
||||||
|
const char* const begin, const char* const end) {
|
||||||
|
T ret;
|
||||||
|
auto [ptr, ec] = std::from_chars(begin, end, ret.value);
|
||||||
|
|
||||||
|
if (ec != std::errc() || ptr != end) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////
|
////////////////
|
||||||
// extract
|
// extract
|
||||||
////////////////
|
////////////////
|
||||||
@@ -103,7 +147,8 @@ struct unsupported_type {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
std::enable_if_t<!std::is_integral_v<T> && !std::is_floating_point_v<T> &&
|
std::enable_if_t<!std::is_integral_v<T> && !std::is_floating_point_v<T> &&
|
||||||
!is_instance_of_v<std::optional, T> &&
|
!is_instance_of_v<std::optional, T> &&
|
||||||
!is_instance_of_v<std::variant, T>,
|
!is_instance_of_v<std::variant, T> &&
|
||||||
|
!is_instance_of_v<numeric_wrapper, T>,
|
||||||
bool>
|
bool>
|
||||||
extract(const char*, const char*, T&) {
|
extract(const char*, const char*, T&) {
|
||||||
static_assert(error::unsupported_type<T>::value,
|
static_assert(error::unsupported_type<T>::value,
|
||||||
@@ -112,7 +157,9 @@ extract(const char*, const char*, T&) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::enable_if_t<std::is_integral_v<T> || std::is_floating_point_v<T>, bool>
|
std::enable_if_t<std::is_integral_v<T> || std::is_floating_point_v<T> ||
|
||||||
|
is_instance_of_v<numeric_wrapper, T>,
|
||||||
|
bool>
|
||||||
extract(const char* begin, const char* end, T& value) {
|
extract(const char* begin, const char* end, T& value) {
|
||||||
auto optional_value = to_num<T>(begin, end);
|
auto optional_value = to_num<T>(begin, end);
|
||||||
if (!optional_value) {
|
if (!optional_value) {
|
||||||
@@ -169,9 +216,9 @@ inline bool extract(const char* begin, const char* end, bool& value) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
size_t size = end - begin;
|
size_t size = end - begin;
|
||||||
if (size == 4 && strncmp(begin, "true", size) == 0) {
|
if (size == 4 && std::strncmp(begin, "true", size) == 0) {
|
||||||
value = true;
|
value = true;
|
||||||
} else if (size == 5 && strncmp(begin, "false", size) == 0) {
|
} else if (size == 5 && std::strncmp(begin, "false", size) == 0) {
|
||||||
value = false;
|
value = false;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -675,7 +675,7 @@ private:
|
|||||||
|
|
||||||
struct reader {
|
struct reader {
|
||||||
reader(const std::string& file_name_, const std::string& delim)
|
reader(const std::string& file_name_, const std::string& delim)
|
||||||
: delim_{delim}, file_{fopen(file_name_.c_str(), "rb")} {
|
: delim_{delim}, file_{std::fopen(file_name_.c_str(), "rb")} {
|
||||||
}
|
}
|
||||||
|
|
||||||
reader(const char* const buffer, size_t csv_data_size,
|
reader(const char* const buffer, size_t csv_data_size,
|
||||||
@@ -736,12 +736,12 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
~reader() {
|
~reader() {
|
||||||
free(buffer_);
|
std::free(buffer_);
|
||||||
free(next_line_buffer_);
|
std::free(next_line_buffer_);
|
||||||
free(helper_buffer_);
|
std::free(helper_buffer_);
|
||||||
|
|
||||||
if (file_) {
|
if (file_) {
|
||||||
fclose(file_);
|
std::fclose(file_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,22 +752,13 @@ private:
|
|||||||
ssize_t get_line_buffer(char** lineptr, size_t* n,
|
ssize_t get_line_buffer(char** lineptr, size_t* n,
|
||||||
const char* const csv_data_buffer,
|
const char* const csv_data_buffer,
|
||||||
size_t csv_data_size, size_t& curr_char) {
|
size_t csv_data_size, size_t& curr_char) {
|
||||||
if (lineptr == nullptr || n == nullptr ||
|
|
||||||
csv_data_buffer == nullptr) {
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (curr_char >= csv_data_size) {
|
if (curr_char >= csv_data_size) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*lineptr == nullptr || *n < get_line_initial_buffer_size) {
|
if (*lineptr == nullptr || *n < get_line_initial_buffer_size) {
|
||||||
auto new_lineptr = static_cast<char*>(
|
auto new_lineptr = static_cast<char*>(
|
||||||
realloc(*lineptr, get_line_initial_buffer_size));
|
strict_realloc(*lineptr, get_line_initial_buffer_size));
|
||||||
if (new_lineptr == nullptr) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*lineptr = new_lineptr;
|
*lineptr = new_lineptr;
|
||||||
*n = get_line_initial_buffer_size;
|
*n = get_line_initial_buffer_size;
|
||||||
}
|
}
|
||||||
@@ -778,11 +769,7 @@ private:
|
|||||||
size_t new_n = *n * 2;
|
size_t new_n = *n * 2;
|
||||||
|
|
||||||
char* new_lineptr =
|
char* new_lineptr =
|
||||||
static_cast<char*>(realloc(*lineptr, new_n));
|
static_cast<char*>(strict_realloc(*lineptr, new_n));
|
||||||
if (new_lineptr == nullptr) {
|
|
||||||
errno = ENOMEM;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*n = new_n;
|
*n = new_n;
|
||||||
*lineptr = new_lineptr;
|
*lineptr = new_lineptr;
|
||||||
}
|
}
|
||||||
@@ -813,7 +800,7 @@ private:
|
|||||||
if (file_) {
|
if (file_) {
|
||||||
ssize = get_line_file(&next_line_buffer_,
|
ssize = get_line_file(&next_line_buffer_,
|
||||||
&next_line_buffer_size_, file_);
|
&next_line_buffer_size_, file_);
|
||||||
curr_char_ = ftell(file_);
|
curr_char_ = std::ftell(file_);
|
||||||
} else {
|
} else {
|
||||||
ssize = get_line_buffer(&next_line_buffer_,
|
ssize = get_line_buffer(&next_line_buffer_,
|
||||||
&next_line_buffer_size_,
|
&next_line_buffer_size_,
|
||||||
@@ -955,10 +942,7 @@ private:
|
|||||||
size_t second_size) {
|
size_t second_size) {
|
||||||
buffer_size = first_size + second_size + 3;
|
buffer_size = first_size + second_size + 3;
|
||||||
auto new_first = static_cast<char*>(
|
auto new_first = static_cast<char*>(
|
||||||
realloc(static_cast<void*>(first), buffer_size));
|
strict_realloc(static_cast<void*>(first), buffer_size));
|
||||||
if (!new_first) {
|
|
||||||
throw std::bad_alloc{};
|
|
||||||
}
|
|
||||||
|
|
||||||
first = new_first;
|
first = new_first;
|
||||||
std::copy_n(second, second_size + 1, first + first_size);
|
std::copy_n(second, second_size + 1, first + first_size);
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool match(const char* const curr, const std::string& delim) {
|
bool match(const char* const curr, const std::string& delim) {
|
||||||
return strncmp(curr, delim.c_str(), delim.size()) == 0;
|
return std::strncmp(curr, delim.c_str(), delim.size()) == 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t delimiter_size(char) {
|
size_t delimiter_size(char) {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ project(
|
|||||||
'cpp_std=c++17',
|
'cpp_std=c++17',
|
||||||
'buildtype=debugoptimized',
|
'buildtype=debugoptimized',
|
||||||
'wrap_mode=forcefallback'],
|
'wrap_mode=forcefallback'],
|
||||||
version: '1.6.2',
|
version: '1.7.0',
|
||||||
meson_version:'>=0.54.0')
|
meson_version:'>=0.54.0')
|
||||||
|
|
||||||
fast_float_dep = dependency('fast_float')
|
fast_float_dep = dependency('fast_float')
|
||||||
|
|||||||
132
ssp.hpp
132
ssp.hpp
@@ -640,6 +640,15 @@ inline void assert_throw_on_error_not_defined() {
|
|||||||
"'throw_on_error' is enabled");
|
"'throw_on_error' is enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void* strict_realloc(void* ptr, size_t size) {
|
||||||
|
ptr = std::realloc(ptr, size);
|
||||||
|
if (!ptr) {
|
||||||
|
throw std::bad_alloc{};
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
#if __unix__
|
#if __unix__
|
||||||
inline ssize_t get_line_file(char** lineptr, size_t* n, FILE* stream) {
|
inline ssize_t get_line_file(char** lineptr, size_t* n, FILE* stream) {
|
||||||
return getline(lineptr, n, stream);
|
return getline(lineptr, n, stream);
|
||||||
@@ -658,37 +667,24 @@ ssize_t get_line_file(char** lineptr, size_t* n, FILE* fp) {
|
|||||||
|
|
||||||
if (*lineptr == nullptr || *n < sizeof(buff)) {
|
if (*lineptr == nullptr || *n < sizeof(buff)) {
|
||||||
size_t new_n = sizeof(buff);
|
size_t new_n = sizeof(buff);
|
||||||
auto new_lineptr = static_cast<char*>(realloc(*lineptr, new_n));
|
*lineptr = static_cast<char*>(strict_realloc(*lineptr, new_n));
|
||||||
if (new_lineptr == nullptr) {
|
|
||||||
errno = ENOMEM;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
*lineptr = new_lineptr;
|
|
||||||
*n = new_n;
|
*n = new_n;
|
||||||
}
|
}
|
||||||
|
|
||||||
(*lineptr)[0] = '\0';
|
(*lineptr)[0] = '\0';
|
||||||
|
|
||||||
size_t line_used = 0;
|
size_t line_used = 0;
|
||||||
while (fgets(buff, sizeof(buff), fp) != nullptr) {
|
while (std::fgets(buff, sizeof(buff), fp) != nullptr) {
|
||||||
line_used = strlen(*lineptr);
|
line_used = std::strlen(*lineptr);
|
||||||
size_t buff_used = strlen(buff);
|
size_t buff_used = std::strlen(buff);
|
||||||
|
|
||||||
if (*n <= buff_used + line_used) {
|
if (*n <= buff_used + line_used) {
|
||||||
size_t new_n = *n * 2;
|
size_t new_n = *n * 2;
|
||||||
|
*lineptr = static_cast<char*>(strict_realloc(*lineptr, new_n));
|
||||||
auto new_lineptr = static_cast<char*>(realloc(*lineptr, new_n));
|
|
||||||
if (new_lineptr == nullptr) {
|
|
||||||
errno = ENOMEM;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
*lineptr = new_lineptr;
|
|
||||||
*n = new_n;
|
*n = new_n;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(*lineptr + line_used, buff, buff_used);
|
std::memcpy(*lineptr + line_used, buff, buff_used);
|
||||||
line_used += buff_used;
|
line_used += buff_used;
|
||||||
(*lineptr)[line_used] = '\0';
|
(*lineptr)[line_used] = '\0';
|
||||||
|
|
||||||
@@ -1186,7 +1182,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool match(const char* const curr, const std::string& delim) {
|
bool match(const char* const curr, const std::string& delim) {
|
||||||
return strncmp(curr, delim.c_str(), delim.size()) == 0;
|
return std::strncmp(curr, delim.c_str(), delim.size()) == 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t delimiter_size(char) {
|
size_t delimiter_size(char) {
|
||||||
@@ -1534,6 +1530,38 @@ std::enable_if_t<std::is_floating_point_v<T>, std::optional<T>> to_num(
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
////////////////
|
||||||
|
// numeric_wrapper
|
||||||
|
////////////////
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct numeric_wrapper {
|
||||||
|
using type = T;
|
||||||
|
|
||||||
|
numeric_wrapper() = default;
|
||||||
|
numeric_wrapper(numeric_wrapper&&) = default;
|
||||||
|
numeric_wrapper(const numeric_wrapper&) = default;
|
||||||
|
|
||||||
|
numeric_wrapper& operator=(numeric_wrapper&&) = default;
|
||||||
|
numeric_wrapper& operator=(const numeric_wrapper&) = default;
|
||||||
|
|
||||||
|
numeric_wrapper(T other) : value{other} {
|
||||||
|
}
|
||||||
|
|
||||||
|
operator T() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator T() const {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
T value;
|
||||||
|
};
|
||||||
|
|
||||||
|
using int8 = numeric_wrapper<int8_t>;
|
||||||
|
using uint8 = numeric_wrapper<uint8_t>;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::enable_if_t<std::is_integral_v<T>, std::optional<T>> to_num(
|
std::enable_if_t<std::is_integral_v<T>, std::optional<T>> to_num(
|
||||||
const char* const begin, const char* const end) {
|
const char* const begin, const char* const end) {
|
||||||
@@ -1546,6 +1574,18 @@ std::enable_if_t<std::is_integral_v<T>, std::optional<T>> to_num(
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::enable_if_t<is_instance_of_v<numeric_wrapper, T>, std::optional<T>> to_num(
|
||||||
|
const char* const begin, const char* const end) {
|
||||||
|
T ret;
|
||||||
|
auto [ptr, ec] = std::from_chars(begin, end, ret.value);
|
||||||
|
|
||||||
|
if (ec != std::errc() || ptr != end) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////
|
////////////////
|
||||||
// extract
|
// extract
|
||||||
////////////////
|
////////////////
|
||||||
@@ -1560,7 +1600,8 @@ struct unsupported_type {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
std::enable_if_t<!std::is_integral_v<T> && !std::is_floating_point_v<T> &&
|
std::enable_if_t<!std::is_integral_v<T> && !std::is_floating_point_v<T> &&
|
||||||
!is_instance_of_v<std::optional, T> &&
|
!is_instance_of_v<std::optional, T> &&
|
||||||
!is_instance_of_v<std::variant, T>,
|
!is_instance_of_v<std::variant, T> &&
|
||||||
|
!is_instance_of_v<numeric_wrapper, T>,
|
||||||
bool>
|
bool>
|
||||||
extract(const char*, const char*, T&) {
|
extract(const char*, const char*, T&) {
|
||||||
static_assert(error::unsupported_type<T>::value,
|
static_assert(error::unsupported_type<T>::value,
|
||||||
@@ -1569,7 +1610,9 @@ extract(const char*, const char*, T&) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::enable_if_t<std::is_integral_v<T> || std::is_floating_point_v<T>, bool>
|
std::enable_if_t<std::is_integral_v<T> || std::is_floating_point_v<T> ||
|
||||||
|
is_instance_of_v<numeric_wrapper, T>,
|
||||||
|
bool>
|
||||||
extract(const char* begin, const char* end, T& value) {
|
extract(const char* begin, const char* end, T& value) {
|
||||||
auto optional_value = to_num<T>(begin, end);
|
auto optional_value = to_num<T>(begin, end);
|
||||||
if (!optional_value) {
|
if (!optional_value) {
|
||||||
@@ -1626,9 +1669,9 @@ inline bool extract(const char* begin, const char* end, bool& value) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
size_t size = end - begin;
|
size_t size = end - begin;
|
||||||
if (size == 4 && strncmp(begin, "true", size) == 0) {
|
if (size == 4 && std::strncmp(begin, "true", size) == 0) {
|
||||||
value = true;
|
value = true;
|
||||||
} else if (size == 5 && strncmp(begin, "false", size) == 0) {
|
} else if (size == 5 && std::strncmp(begin, "false", size) == 0) {
|
||||||
value = false;
|
value = false;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -2235,7 +2278,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t line() const {
|
size_t line() const {
|
||||||
return reader_.line_number_ > 1 ? reader_.line_number_ - 1
|
return reader_.line_number_ > 0 ? reader_.line_number_ - 1
|
||||||
: reader_.line_number_;
|
: reader_.line_number_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2347,7 +2390,7 @@ public:
|
|||||||
reader_.next_line_converter_.set_column_mapping(column_mappings,
|
reader_.next_line_converter_.set_column_mapping(column_mappings,
|
||||||
header_.size());
|
header_.size());
|
||||||
|
|
||||||
if (line() == 1) {
|
if (line() == 0) {
|
||||||
ignore_next();
|
ignore_next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2808,7 +2851,7 @@ private:
|
|||||||
|
|
||||||
struct reader {
|
struct reader {
|
||||||
reader(const std::string& file_name_, const std::string& delim)
|
reader(const std::string& file_name_, const std::string& delim)
|
||||||
: delim_{delim}, file_{fopen(file_name_.c_str(), "rb")} {
|
: delim_{delim}, file_{std::fopen(file_name_.c_str(), "rb")} {
|
||||||
}
|
}
|
||||||
|
|
||||||
reader(const char* const buffer, size_t csv_data_size,
|
reader(const char* const buffer, size_t csv_data_size,
|
||||||
@@ -2830,8 +2873,7 @@ private:
|
|||||||
csv_data_buffer_{other.csv_data_buffer_},
|
csv_data_buffer_{other.csv_data_buffer_},
|
||||||
csv_data_size_{other.csv_data_size_},
|
csv_data_size_{other.csv_data_size_},
|
||||||
curr_char_{other.curr_char_}, crlf_{other.crlf_},
|
curr_char_{other.curr_char_}, crlf_{other.crlf_},
|
||||||
line_number_{other.line_number_},
|
line_number_{other.line_number_}, chars_read_{other.chars_read_},
|
||||||
chars_read_{other.chars_read_},
|
|
||||||
next_line_size_{other.next_line_size_} {
|
next_line_size_{other.next_line_size_} {
|
||||||
other.buffer_ = nullptr;
|
other.buffer_ = nullptr;
|
||||||
other.next_line_buffer_ = nullptr;
|
other.next_line_buffer_ = nullptr;
|
||||||
@@ -2870,12 +2912,12 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
~reader() {
|
~reader() {
|
||||||
free(buffer_);
|
std::free(buffer_);
|
||||||
free(next_line_buffer_);
|
std::free(next_line_buffer_);
|
||||||
free(helper_buffer_);
|
std::free(helper_buffer_);
|
||||||
|
|
||||||
if (file_) {
|
if (file_) {
|
||||||
fclose(file_);
|
std::fclose(file_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2886,22 +2928,13 @@ private:
|
|||||||
ssize_t get_line_buffer(char** lineptr, size_t* n,
|
ssize_t get_line_buffer(char** lineptr, size_t* n,
|
||||||
const char* const csv_data_buffer,
|
const char* const csv_data_buffer,
|
||||||
size_t csv_data_size, size_t& curr_char) {
|
size_t csv_data_size, size_t& curr_char) {
|
||||||
if (lineptr == nullptr || n == nullptr ||
|
|
||||||
csv_data_buffer == nullptr) {
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (curr_char >= csv_data_size) {
|
if (curr_char >= csv_data_size) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*lineptr == nullptr || *n < get_line_initial_buffer_size) {
|
if (*lineptr == nullptr || *n < get_line_initial_buffer_size) {
|
||||||
auto new_lineptr = static_cast<char*>(
|
auto new_lineptr = static_cast<char*>(
|
||||||
realloc(*lineptr, get_line_initial_buffer_size));
|
strict_realloc(*lineptr, get_line_initial_buffer_size));
|
||||||
if (new_lineptr == nullptr) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*lineptr = new_lineptr;
|
*lineptr = new_lineptr;
|
||||||
*n = get_line_initial_buffer_size;
|
*n = get_line_initial_buffer_size;
|
||||||
}
|
}
|
||||||
@@ -2912,11 +2945,7 @@ private:
|
|||||||
size_t new_n = *n * 2;
|
size_t new_n = *n * 2;
|
||||||
|
|
||||||
char* new_lineptr =
|
char* new_lineptr =
|
||||||
static_cast<char*>(realloc(*lineptr, new_n));
|
static_cast<char*>(strict_realloc(*lineptr, new_n));
|
||||||
if (new_lineptr == nullptr) {
|
|
||||||
errno = ENOMEM;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*n = new_n;
|
*n = new_n;
|
||||||
*lineptr = new_lineptr;
|
*lineptr = new_lineptr;
|
||||||
}
|
}
|
||||||
@@ -2947,7 +2976,7 @@ private:
|
|||||||
if (file_) {
|
if (file_) {
|
||||||
ssize = get_line_file(&next_line_buffer_,
|
ssize = get_line_file(&next_line_buffer_,
|
||||||
&next_line_buffer_size_, file_);
|
&next_line_buffer_size_, file_);
|
||||||
curr_char_ = ftell(file_);
|
curr_char_ = std::ftell(file_);
|
||||||
} else {
|
} else {
|
||||||
ssize = get_line_buffer(&next_line_buffer_,
|
ssize = get_line_buffer(&next_line_buffer_,
|
||||||
&next_line_buffer_size_,
|
&next_line_buffer_size_,
|
||||||
@@ -3089,10 +3118,7 @@ private:
|
|||||||
size_t second_size) {
|
size_t second_size) {
|
||||||
buffer_size = first_size + second_size + 3;
|
buffer_size = first_size + second_size + 3;
|
||||||
auto new_first = static_cast<char*>(
|
auto new_first = static_cast<char*>(
|
||||||
realloc(static_cast<void*>(first), buffer_size));
|
strict_realloc(static_cast<void*>(first), buffer_size));
|
||||||
if (!new_first) {
|
|
||||||
throw std::bad_alloc{};
|
|
||||||
}
|
|
||||||
|
|
||||||
first = new_first;
|
first = new_first;
|
||||||
std::copy_n(second, second_size + 1, first + first_size);
|
std::copy_n(second, second_size + 1, first + first_size);
|
||||||
|
|||||||
@@ -46,90 +46,88 @@ TEST_CASE("converter test split with exceptions") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("converter test valid conversions") {
|
TEST_CASE_TEMPLATE("converter test valid conversions", T, int, ss::uint8) {
|
||||||
ss::converter c;
|
ss::converter c;
|
||||||
|
|
||||||
{
|
{
|
||||||
auto tup = c.convert<int>("5");
|
auto tup = c.convert<T>("5");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, 5);
|
CHECK_EQ(tup, 5);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup = c.convert<int, void>("5,junk");
|
auto tup = c.convert<T, void>("5,junk");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, 5);
|
CHECK_EQ(tup, 5);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup = c.convert<void, int>("junk,5");
|
auto tup = c.convert<void, T>("junk,5");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, 5);
|
CHECK_EQ(tup, 5);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup = c.convert<int, void, void>("5\njunk\njunk", "\n");
|
auto tup = c.convert<T, void, void>("5\njunk\njunk", "\n");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, 5);
|
CHECK_EQ(tup, 5);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup = c.convert<void, int, void>("junk 5 junk", " ");
|
auto tup = c.convert<void, T, void>("junk 5 junk", " ");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, 5);
|
CHECK_EQ(tup, 5);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup = c.convert<void, void, int>("junk\tjunk\t5", "\t");
|
auto tup = c.convert<void, void, T>("junk\tjunk\t5", "\t");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, 5);
|
CHECK_EQ(tup, 5);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup =
|
auto tup =
|
||||||
c.convert<void, void, std::optional<int>>("junk\tjunk\t5", "\t");
|
c.convert<void, void, std::optional<T>>("junk\tjunk\t5", "\t");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
REQUIRE(tup.has_value());
|
REQUIRE(tup.has_value());
|
||||||
CHECK_EQ(tup, 5);
|
CHECK_EQ(tup, 5);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup = c.convert<int, double, void>("5,6.6,junk");
|
auto tup = c.convert<T, double, void>("5,6.6,junk");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, std::make_tuple(5, 6.6));
|
CHECK_EQ(tup, std::make_tuple(5, 6.6));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup = c.convert<int, void, double>("5,junk,6.6");
|
auto tup = c.convert<T, void, double>("5,junk,6.6");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, std::make_tuple(5, 6.6));
|
CHECK_EQ(tup, std::make_tuple(5, 6.6));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup = c.convert<void, int, double>("junk;5;6.6", ";");
|
auto tup = c.convert<void, T, double>("junk;5;6.6", ";");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, std::make_tuple(5, 6.6));
|
CHECK_EQ(tup, std::make_tuple(5, 6.6));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup =
|
auto tup = c.convert<void, std::optional<T>, double>("junk;5;6.6", ";");
|
||||||
c.convert<void, std::optional<int>, double>("junk;5;6.6", ";");
|
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
REQUIRE(std::get<0>(tup).has_value());
|
REQUIRE(std::get<0>(tup).has_value());
|
||||||
CHECK_EQ(tup, std::make_tuple(5, 6.6));
|
CHECK_EQ(tup, std::make_tuple(5, 6.6));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup =
|
auto tup =
|
||||||
c.convert<void, std::optional<int>, double>("junk;5.4;6.6", ";");
|
c.convert<void, std::optional<T>, double>("junk;5.4;6.6", ";");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
REQUIRE_FALSE(std::get<0>(tup).has_value());
|
REQUIRE_FALSE(std::get<0>(tup).has_value());
|
||||||
CHECK_EQ(tup, std::make_tuple(std::optional<int>{}, 6.6));
|
CHECK_EQ(tup, std::make_tuple(std::optional<T>{}, 6.6));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup =
|
auto tup =
|
||||||
c.convert<void, std::variant<int, double>, double>("junk;5;6.6",
|
c.convert<void, std::variant<T, double>, double>("junk;5;6.6", ";");
|
||||||
";");
|
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
REQUIRE(std::holds_alternative<int>(std::get<0>(tup)));
|
REQUIRE(std::holds_alternative<T>(std::get<0>(tup)));
|
||||||
CHECK_EQ(tup, std::make_tuple(std::variant<int, double>{5}, 6.6));
|
CHECK_EQ(tup, std::make_tuple(std::variant<T, double>{T(5)}, 6.6));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup =
|
auto tup =
|
||||||
c.convert<void, std::variant<int, double>, double>("junk;5.5;6.6",
|
c.convert<void, std::variant<T, double>, double>("junk;5.5;6.6",
|
||||||
";");
|
";");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
REQUIRE(std::holds_alternative<double>(std::get<0>(tup)));
|
REQUIRE(std::holds_alternative<double>(std::get<0>(tup)));
|
||||||
CHECK_EQ(tup, std::make_tuple(std::variant<int, double>{5.5}, 6.6));
|
CHECK_EQ(tup, std::make_tuple(std::variant<T, double>{5.5}, 6.6));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup = c.convert<void, std::string_view, double,
|
auto tup = c.convert<void, std::string_view, double,
|
||||||
@@ -140,11 +138,12 @@ TEST_CASE("converter test valid conversions") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("converter test valid conversions with exceptions") {
|
TEST_CASE_TEMPLATE("converter test valid conversions with exceptions", T, int,
|
||||||
|
ss::uint8) {
|
||||||
ss::converter<ss::throw_on_error> c;
|
ss::converter<ss::throw_on_error> c;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto tup = c.convert<int>("5");
|
auto tup = c.convert<T>("5");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, 5);
|
CHECK_EQ(tup, 5);
|
||||||
} catch (ss::exception& e) {
|
} catch (ss::exception& e) {
|
||||||
@@ -152,7 +151,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto tup = c.convert<int, void>("5,junk");
|
auto tup = c.convert<T, void>("5,junk");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, 5);
|
CHECK_EQ(tup, 5);
|
||||||
} catch (ss::exception& e) {
|
} catch (ss::exception& e) {
|
||||||
@@ -160,7 +159,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto tup = c.convert<void, int>("junk,5");
|
auto tup = c.convert<void, T>("junk,5");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, 5);
|
CHECK_EQ(tup, 5);
|
||||||
} catch (ss::exception& e) {
|
} catch (ss::exception& e) {
|
||||||
@@ -168,7 +167,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto tup = c.convert<int, void, void>("5\njunk\njunk", "\n");
|
auto tup = c.convert<T, void, void>("5\njunk\njunk", "\n");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, 5);
|
CHECK_EQ(tup, 5);
|
||||||
} catch (ss::exception& e) {
|
} catch (ss::exception& e) {
|
||||||
@@ -176,7 +175,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto tup = c.convert<void, int, void>("junk 5 junk", " ");
|
auto tup = c.convert<void, T, void>("junk 5 junk", " ");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, 5);
|
CHECK_EQ(tup, 5);
|
||||||
} catch (ss::exception& e) {
|
} catch (ss::exception& e) {
|
||||||
@@ -184,7 +183,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto tup = c.convert<void, void, int>("junk\tjunk\t5", "\t");
|
auto tup = c.convert<void, void, T>("junk\tjunk\t5", "\t");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, 5);
|
CHECK_EQ(tup, 5);
|
||||||
} catch (ss::exception& e) {
|
} catch (ss::exception& e) {
|
||||||
@@ -193,7 +192,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
auto tup =
|
auto tup =
|
||||||
c.convert<void, void, std::optional<int>>("junk\tjunk\t5", "\t");
|
c.convert<void, void, std::optional<T>>("junk\tjunk\t5", "\t");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
REQUIRE(tup.has_value());
|
REQUIRE(tup.has_value());
|
||||||
CHECK_EQ(tup, 5);
|
CHECK_EQ(tup, 5);
|
||||||
@@ -202,7 +201,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto tup = c.convert<int, double, void>("5,6.6,junk");
|
auto tup = c.convert<T, double, void>("5,6.6,junk");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, std::make_tuple(5, 6.6));
|
CHECK_EQ(tup, std::make_tuple(5, 6.6));
|
||||||
} catch (ss::exception& e) {
|
} catch (ss::exception& e) {
|
||||||
@@ -210,7 +209,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto tup = c.convert<int, void, double>("5,junk,6.6");
|
auto tup = c.convert<T, void, double>("5,junk,6.6");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, std::make_tuple(5, 6.6));
|
CHECK_EQ(tup, std::make_tuple(5, 6.6));
|
||||||
} catch (ss::exception& e) {
|
} catch (ss::exception& e) {
|
||||||
@@ -218,7 +217,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto tup = c.convert<void, int, double>("junk;5;6.6", ";");
|
auto tup = c.convert<void, T, double>("junk;5;6.6", ";");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, std::make_tuple(5, 6.6));
|
CHECK_EQ(tup, std::make_tuple(5, 6.6));
|
||||||
} catch (ss::exception& e) {
|
} catch (ss::exception& e) {
|
||||||
@@ -226,8 +225,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto tup =
|
auto tup = c.convert<void, std::optional<T>, double>("junk;5;6.6", ";");
|
||||||
c.convert<void, std::optional<int>, double>("junk;5;6.6", ";");
|
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
REQUIRE(std::get<0>(tup).has_value());
|
REQUIRE(std::get<0>(tup).has_value());
|
||||||
CHECK_EQ(tup, std::make_tuple(5, 6.6));
|
CHECK_EQ(tup, std::make_tuple(5, 6.6));
|
||||||
@@ -237,32 +235,31 @@ TEST_CASE("converter test valid conversions with exceptions") {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
auto tup =
|
auto tup =
|
||||||
c.convert<void, std::optional<int>, double>("junk;5.4;6.6", ";");
|
c.convert<void, std::optional<T>, double>("junk;5.4;6.6", ";");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
REQUIRE_FALSE(std::get<0>(tup).has_value());
|
REQUIRE_FALSE(std::get<0>(tup).has_value());
|
||||||
CHECK_EQ(tup, std::make_tuple(std::optional<int>{}, 6.6));
|
CHECK_EQ(tup, std::make_tuple(std::optional<T>{}, 6.6));
|
||||||
} catch (ss::exception& e) {
|
} catch (ss::exception& e) {
|
||||||
FAIL(std::string{e.what()});
|
FAIL(std::string{e.what()});
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto tup =
|
auto tup =
|
||||||
c.convert<void, std::variant<int, double>, double>("junk;5;6.6",
|
c.convert<void, std::variant<T, double>, double>("junk;5;6.6", ";");
|
||||||
";");
|
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
REQUIRE(std::holds_alternative<int>(std::get<0>(tup)));
|
REQUIRE(std::holds_alternative<T>(std::get<0>(tup)));
|
||||||
CHECK_EQ(tup, std::make_tuple(std::variant<int, double>{5}, 6.6));
|
CHECK_EQ(tup, std::make_tuple(std::variant<T, double>{T(5)}, 6.6));
|
||||||
} catch (ss::exception& e) {
|
} catch (ss::exception& e) {
|
||||||
FAIL(std::string{e.what()});
|
FAIL(std::string{e.what()});
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto tup =
|
auto tup =
|
||||||
c.convert<void, std::variant<int, double>, double>("junk;5.5;6.6",
|
c.convert<void, std::variant<T, double>, double>("junk;5.5;6.6",
|
||||||
";");
|
";");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
REQUIRE(std::holds_alternative<double>(std::get<0>(tup)));
|
REQUIRE(std::holds_alternative<double>(std::get<0>(tup)));
|
||||||
CHECK_EQ(tup, std::make_tuple(std::variant<int, double>{5.5}, 6.6));
|
CHECK_EQ(tup, std::make_tuple(std::variant<T, double>{5.5}, 6.6));
|
||||||
} catch (ss::exception& e) {
|
} catch (ss::exception& e) {
|
||||||
FAIL(std::string{e.what()});
|
FAIL(std::string{e.what()});
|
||||||
}
|
}
|
||||||
@@ -278,110 +275,114 @@ TEST_CASE("converter test valid conversions with exceptions") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("converter test invalid conversions") {
|
TEST_CASE_TEMPLATE("converter test invalid conversions", T, int, ss::uint8) {
|
||||||
ss::converter c;
|
ss::converter c;
|
||||||
|
|
||||||
c.convert<int>("");
|
c.convert<T>("");
|
||||||
REQUIRE_FALSE(c.valid());
|
REQUIRE_FALSE(c.valid());
|
||||||
|
|
||||||
c.convert<int>("1", "");
|
c.convert<T>("1", "");
|
||||||
REQUIRE_FALSE(c.valid());
|
REQUIRE_FALSE(c.valid());
|
||||||
|
|
||||||
c.convert<int>("10", "");
|
c.convert<T>("10", "");
|
||||||
REQUIRE_FALSE(c.valid());
|
REQUIRE_FALSE(c.valid());
|
||||||
|
|
||||||
c.convert<int, void>("");
|
c.convert<T, void>("");
|
||||||
REQUIRE_FALSE(c.valid());
|
REQUIRE_FALSE(c.valid());
|
||||||
|
|
||||||
c.convert<int, void>(",junk");
|
c.convert<T, void>(",junk");
|
||||||
REQUIRE_FALSE(c.valid());
|
REQUIRE_FALSE(c.valid());
|
||||||
|
|
||||||
c.convert<void, int>("junk,");
|
c.convert<void, T>("junk,");
|
||||||
REQUIRE_FALSE(c.valid());
|
REQUIRE_FALSE(c.valid());
|
||||||
|
|
||||||
c.convert<int>("x");
|
c.convert<T>("x");
|
||||||
REQUIRE_FALSE(c.valid());
|
REQUIRE_FALSE(c.valid());
|
||||||
|
|
||||||
c.convert<int, void>("x");
|
c.convert<T, void>("x");
|
||||||
REQUIRE_FALSE(c.valid());
|
REQUIRE_FALSE(c.valid());
|
||||||
|
|
||||||
c.convert<int, void>("x,junk");
|
c.convert<T, void>("x,junk");
|
||||||
REQUIRE_FALSE(c.valid());
|
REQUIRE_FALSE(c.valid());
|
||||||
|
|
||||||
c.convert<void, int>("junk,x");
|
c.convert<void, T>("junk,x");
|
||||||
REQUIRE_FALSE(c.valid());
|
REQUIRE_FALSE(c.valid());
|
||||||
|
|
||||||
c.convert<void, std::variant<int, double>, double>("junk;.5.5;6", ";");
|
c.convert<void, std::variant<T, double>, double>("junk;.5.5;6", ";");
|
||||||
REQUIRE_FALSE(c.valid());
|
REQUIRE_FALSE(c.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("converter test invalid conversions with exceptions") {
|
TEST_CASE_TEMPLATE("converter test invalid conversions with exceptions", T, int,
|
||||||
|
ss::uint8) {
|
||||||
ss::converter<ss::throw_on_error> c;
|
ss::converter<ss::throw_on_error> c;
|
||||||
|
|
||||||
REQUIRE_EXCEPTION(c.convert<int>(""));
|
REQUIRE_EXCEPTION(c.convert<T>(""));
|
||||||
REQUIRE_EXCEPTION(c.convert<int>("1", ""));
|
REQUIRE_EXCEPTION(c.convert<T>("1", ""));
|
||||||
REQUIRE_EXCEPTION(c.convert<int>("10", ""));
|
REQUIRE_EXCEPTION(c.convert<T>("10", ""));
|
||||||
REQUIRE_EXCEPTION(c.convert<int, void>(""));
|
REQUIRE_EXCEPTION(c.convert<T, void>(""));
|
||||||
REQUIRE_EXCEPTION(c.convert<int, void>(",junk"));
|
REQUIRE_EXCEPTION(c.convert<T, void>(",junk"));
|
||||||
REQUIRE_EXCEPTION(c.convert<void, int>("junk,"));
|
REQUIRE_EXCEPTION(c.convert<void, T>("junk,"));
|
||||||
REQUIRE_EXCEPTION(c.convert<int>("x"));
|
REQUIRE_EXCEPTION(c.convert<T>("x"));
|
||||||
REQUIRE_EXCEPTION(c.convert<int, void>("x"));
|
REQUIRE_EXCEPTION(c.convert<T, void>("x"));
|
||||||
REQUIRE_EXCEPTION(c.convert<int, void>("x,junk"));
|
REQUIRE_EXCEPTION(c.convert<T, void>("x,junk"));
|
||||||
REQUIRE_EXCEPTION(c.convert<void, int>("junk,x"));
|
REQUIRE_EXCEPTION(c.convert<void, T>("junk,x"));
|
||||||
REQUIRE_EXCEPTION(
|
REQUIRE_EXCEPTION(
|
||||||
c.convert<void, std::variant<int, double>, double>("junk;.5.5;6", ";"));
|
c.convert<void, std::variant<T, double>, double>("junk;.5.5;6", ";"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("converter test ss:ax restriction (all except)") {
|
TEST_CASE_TEMPLATE("converter test ss:ax restriction (all except)", T, int,
|
||||||
|
ss::uint8) {
|
||||||
ss::converter c;
|
ss::converter c;
|
||||||
|
|
||||||
c.convert<ss::ax<int, 0>>("0");
|
c.convert<ss::ax<T, 0>>("0");
|
||||||
REQUIRE_FALSE(c.valid());
|
REQUIRE_FALSE(c.valid());
|
||||||
|
|
||||||
c.convert<ss::ax<int, 0, 1, 2>>("1");
|
c.convert<ss::ax<T, 0, 1, 2>>("1");
|
||||||
REQUIRE_FALSE(c.valid());
|
REQUIRE_FALSE(c.valid());
|
||||||
|
|
||||||
c.convert<void, char, ss::ax<int, 0, 1, 2>>("junk,c,1");
|
c.convert<void, char, ss::ax<T, 0, 1, 2>>("junk,c,1");
|
||||||
REQUIRE_FALSE(c.valid());
|
REQUIRE_FALSE(c.valid());
|
||||||
|
|
||||||
c.convert<ss::ax<int, 1>, char>("1,c");
|
c.convert<ss::ax<T, 1>, char>("1,c");
|
||||||
REQUIRE_FALSE(c.valid());
|
REQUIRE_FALSE(c.valid());
|
||||||
{
|
{
|
||||||
int tup = c.convert<ss::ax<int, 1>>("3");
|
T tup = c.convert<ss::ax<T, 1>>("3");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, 3);
|
CHECK_EQ(tup, 3);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::tuple<char, int> tup = c.convert<char, ss::ax<int, 1>>("c,3");
|
std::tuple<char, T> tup = c.convert<char, ss::ax<T, 1>>("c,3");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, std::make_tuple('c', 3));
|
CHECK_EQ(tup, std::make_tuple('c', 3));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::tuple<int, char> tup = c.convert<ss::ax<int, 1>, char>("3,c");
|
std::tuple<T, char> tup = c.convert<ss::ax<T, 1>, char>("3,c");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, std::make_tuple(3, 'c'));
|
CHECK_EQ(tup, std::make_tuple(3, 'c'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("converter test ss:ax restriction (all except) with exceptions") {
|
TEST_CASE_TEMPLATE(
|
||||||
|
"converter test ss:ax restriction (all except) with exceptions", T, int,
|
||||||
|
ss::uint8) {
|
||||||
ss::converter<ss::throw_on_error> c;
|
ss::converter<ss::throw_on_error> c;
|
||||||
|
|
||||||
REQUIRE_EXCEPTION(c.convert<ss::ax<int, 0>>("0"));
|
REQUIRE_EXCEPTION(c.convert<ss::ax<T, 0>>("0"));
|
||||||
REQUIRE_EXCEPTION(c.convert<ss::ax<int, 0, 1, 2>>("1"));
|
REQUIRE_EXCEPTION(c.convert<ss::ax<T, 0, 1, 2>>("1"));
|
||||||
REQUIRE_EXCEPTION(c.convert<void, char, ss::ax<int, 0, 1, 2>>("junk,c,1"));
|
REQUIRE_EXCEPTION(c.convert<void, char, ss::ax<T, 0, 1, 2>>("junk,c,1"));
|
||||||
REQUIRE_EXCEPTION(c.convert<ss::ax<int, 1>, char>("1,c"));
|
REQUIRE_EXCEPTION(c.convert<ss::ax<T, 1>, char>("1,c"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
{
|
{
|
||||||
int tup = c.convert<ss::ax<int, 1>>("3");
|
T tup = c.convert<ss::ax<T, 1>>("3");
|
||||||
CHECK_EQ(tup, 3);
|
CHECK_EQ(tup, 3);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::tuple<char, int> tup = c.convert<char, ss::ax<int, 1>>("c,3");
|
std::tuple<char, T> tup = c.convert<char, ss::ax<T, 1>>("c,3");
|
||||||
CHECK_EQ(tup, std::make_tuple('c', 3));
|
CHECK_EQ(tup, std::make_tuple('c', 3));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::tuple<int, char> tup = c.convert<ss::ax<int, 1>, char>("3,c");
|
std::tuple<T, char> tup = c.convert<ss::ax<T, 1>, char>("3,c");
|
||||||
CHECK_EQ(tup, std::make_tuple(3, 'c'));
|
CHECK_EQ(tup, std::make_tuple(3, 'c'));
|
||||||
}
|
}
|
||||||
} catch (ss::exception& e) {
|
} catch (ss::exception& e) {
|
||||||
@@ -456,65 +457,68 @@ TEST_CASE("converter test ss:nx restriction (none except) with exceptions") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("converter test ss:ir restriction (in range)") {
|
TEST_CASE_TEMPLATE("converter test ss:ir restriction (in range)", T, int,
|
||||||
|
ss::uint8) {
|
||||||
ss::converter c;
|
ss::converter c;
|
||||||
|
|
||||||
c.convert<ss::ir<int, 0, 2>>("3");
|
c.convert<ss::ir<T, 0, 2>>("3");
|
||||||
REQUIRE_FALSE(c.valid());
|
REQUIRE_FALSE(c.valid());
|
||||||
|
|
||||||
c.convert<char, ss::ir<int, 4, 69>>("c,3");
|
c.convert<char, ss::ir<T, 4, 69>>("c,3");
|
||||||
REQUIRE_FALSE(c.valid());
|
REQUIRE_FALSE(c.valid());
|
||||||
|
|
||||||
c.convert<ss::ir<int, 1, 2>, char>("3,c");
|
c.convert<ss::ir<T, 1, 2>, char>("3,c");
|
||||||
REQUIRE_FALSE(c.valid());
|
REQUIRE_FALSE(c.valid());
|
||||||
|
|
||||||
{
|
{
|
||||||
auto tup = c.convert<ss::ir<int, 1, 5>>("3");
|
auto tup = c.convert<ss::ir<T, 1, 5>>("3");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, 3);
|
CHECK_EQ(tup, 3);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup = c.convert<ss::ir<int, 0, 2>>("2");
|
auto tup = c.convert<ss::ir<T, 0, 2>>("2");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, 2);
|
CHECK_EQ(tup, 2);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup = c.convert<char, void, ss::ir<int, 0, 1>>("c,junk,1");
|
auto tup = c.convert<char, void, ss::ir<T, 0, 1>>("c,junk,1");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, std::make_tuple('c', 1));
|
CHECK_EQ(tup, std::make_tuple('c', 1));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup = c.convert<ss::ir<int, 1, 20>, char>("1,c");
|
auto tup = c.convert<ss::ir<T, 1, 20>, char>("1,c");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, std::make_tuple(1, 'c'));
|
CHECK_EQ(tup, std::make_tuple(1, 'c'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("converter test ss:ir restriction (in range) with exceptions") {
|
TEST_CASE_TEMPLATE(
|
||||||
|
"converter test ss:ir restriction (in range) with exceptions", T, int,
|
||||||
|
ss::uint8) {
|
||||||
ss::converter<ss::throw_on_error> c;
|
ss::converter<ss::throw_on_error> c;
|
||||||
|
|
||||||
REQUIRE_EXCEPTION(c.convert<ss::ir<int, 0, 2>>("3"));
|
REQUIRE_EXCEPTION(c.convert<ss::ir<T, 0, 2>>("3"));
|
||||||
REQUIRE_EXCEPTION(c.convert<char, ss::ir<int, 4, 69>>("c,3"));
|
REQUIRE_EXCEPTION(c.convert<char, ss::ir<T, 4, 69>>("c,3"));
|
||||||
REQUIRE_EXCEPTION(c.convert<ss::ir<int, 1, 2>, char>("3,c"));
|
REQUIRE_EXCEPTION(c.convert<ss::ir<T, 1, 2>, char>("3,c"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
{
|
{
|
||||||
auto tup = c.convert<ss::ir<int, 1, 5>>("3");
|
auto tup = c.convert<ss::ir<T, 1, 5>>("3");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, 3);
|
CHECK_EQ(tup, 3);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup = c.convert<ss::ir<int, 0, 2>>("2");
|
auto tup = c.convert<ss::ir<T, 0, 2>>("2");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, 2);
|
CHECK_EQ(tup, 2);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup = c.convert<char, void, ss::ir<int, 0, 1>>("c,junk,1");
|
auto tup = c.convert<char, void, ss::ir<T, 0, 1>>("c,junk,1");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, std::make_tuple('c', 1));
|
CHECK_EQ(tup, std::make_tuple('c', 1));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto tup = c.convert<ss::ir<int, 1, 20>, char>("1,c");
|
auto tup = c.convert<ss::ir<T, 1, 20>, char>("1,c");
|
||||||
REQUIRE(c.valid());
|
REQUIRE(c.valid());
|
||||||
CHECK_EQ(tup, std::make_tuple(1, 'c'));
|
CHECK_EQ(tup, std::make_tuple(1, 'c'));
|
||||||
}
|
}
|
||||||
@@ -978,4 +982,3 @@ TEST_CASE("converter test invalid split conversions with exceptions") {
|
|||||||
buff(R"(just,some,2,"strings\")")));
|
buff(R"(just,some,2,"strings\")")));
|
||||||
CHECK(c.unterminated_quote());
|
CHECK(c.unterminated_quote());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,32 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <ss/extract.hpp>
|
#include <ss/extract.hpp>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct numeric_limits : public std::numeric_limits<T> {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct numeric_limits<ss::numeric_wrapper<T>> : public std::numeric_limits<T> {
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_signed : public std::is_signed<T> {};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct is_signed<ss::int8> : public std::true_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_unsigned : public std::is_unsigned<T> {};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct is_unsigned<ss::uint8> : public std::true_type {};
|
||||||
|
|
||||||
|
} /* namespace */
|
||||||
|
|
||||||
|
static_assert(is_signed<ss::int8>::value);
|
||||||
|
static_assert(is_unsigned<ss::uint8>::value);
|
||||||
|
|
||||||
TEST_CASE("testing extract functions for floating point values") {
|
TEST_CASE("testing extract functions for floating point values") {
|
||||||
CHECK_FLOATING_CONVERSION(123.456, float);
|
CHECK_FLOATING_CONVERSION(123.456, float);
|
||||||
CHECK_FLOATING_CONVERSION(123.456, double);
|
CHECK_FLOATING_CONVERSION(123.456, double);
|
||||||
@@ -22,18 +48,18 @@ TEST_CASE("testing extract functions for floating point values") {
|
|||||||
#define CHECK_DECIMAL_CONVERSION(input, type) \
|
#define CHECK_DECIMAL_CONVERSION(input, type) \
|
||||||
{ \
|
{ \
|
||||||
std::string s = #input; \
|
std::string s = #input; \
|
||||||
auto t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
|
type value; \
|
||||||
REQUIRE(t.has_value()); \
|
bool valid = ss::extract(s.c_str(), s.c_str() + s.size(), value); \
|
||||||
CHECK_EQ(t.value(), type(input)); \
|
REQUIRE(valid); \
|
||||||
|
CHECK_EQ(value, type(input)); \
|
||||||
} \
|
} \
|
||||||
{ \
|
/* check negative too */ \
|
||||||
/* check negative too */ \
|
if (is_signed<type>::value) { \
|
||||||
if (std::is_signed_v<type>) { \
|
std::string s = std::string("-") + #input; \
|
||||||
auto s = std::string("-") + #input; \
|
type value; \
|
||||||
auto t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
|
bool valid = ss::extract(s.c_str(), s.c_str() + s.size(), value); \
|
||||||
REQUIRE(t.has_value()); \
|
REQUIRE(valid); \
|
||||||
CHECK_EQ(t.value(), type(-input)); \
|
CHECK_EQ(value, type(-input)); \
|
||||||
} \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using us = unsigned short;
|
using us = unsigned short;
|
||||||
@@ -43,6 +69,8 @@ using ll = long long;
|
|||||||
using ull = unsigned long long;
|
using ull = unsigned long long;
|
||||||
|
|
||||||
TEST_CASE("extract test functions for decimal values") {
|
TEST_CASE("extract test functions for decimal values") {
|
||||||
|
CHECK_DECIMAL_CONVERSION(12, ss::int8);
|
||||||
|
CHECK_DECIMAL_CONVERSION(12, ss::uint8);
|
||||||
CHECK_DECIMAL_CONVERSION(1234, short);
|
CHECK_DECIMAL_CONVERSION(1234, short);
|
||||||
CHECK_DECIMAL_CONVERSION(1234, us);
|
CHECK_DECIMAL_CONVERSION(1234, us);
|
||||||
CHECK_DECIMAL_CONVERSION(1234, int);
|
CHECK_DECIMAL_CONVERSION(1234, int);
|
||||||
@@ -54,6 +82,9 @@ TEST_CASE("extract test functions for decimal values") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("extract test functions for numbers with invalid inputs") {
|
TEST_CASE("extract test functions for numbers with invalid inputs") {
|
||||||
|
// negative unsigned value for numeric_wrapper
|
||||||
|
CHECK_INVALID_CONVERSION("-12", ss::uint8);
|
||||||
|
|
||||||
// negative unsigned value
|
// negative unsigned value
|
||||||
CHECK_INVALID_CONVERSION("-1234", ul);
|
CHECK_INVALID_CONVERSION("-1234", ul);
|
||||||
|
|
||||||
@@ -70,46 +101,38 @@ TEST_CASE("extract test functions for numbers with invalid inputs") {
|
|||||||
CHECK_INVALID_CONVERSION("", int);
|
CHECK_INVALID_CONVERSION("", int);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_OUT_OF_RANGE_CONVERSION(type) \
|
TEST_CASE_TEMPLATE(
|
||||||
{ \
|
"extract test functions for numbers with out of range inputs", T, short, us,
|
||||||
std::string s = std::to_string(std::numeric_limits<type>::max()); \
|
int, ui, long, ul, ll, ull, ss::uint8) {
|
||||||
auto t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
|
{
|
||||||
CHECK(t.has_value()); \
|
std::string s = std::to_string(numeric_limits<T>::max());
|
||||||
for (auto& i : s) { \
|
auto t = ss::to_num<T>(s.c_str(), s.c_str() + s.size());
|
||||||
if (i != '9' && i != '.') { \
|
CHECK(t.has_value());
|
||||||
i = '9'; \
|
for (auto& i : s) {
|
||||||
break; \
|
if (i != '9' && i != '.') {
|
||||||
} \
|
i = '9';
|
||||||
} \
|
break;
|
||||||
t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
|
}
|
||||||
CHECK_FALSE(t.has_value()); \
|
}
|
||||||
} \
|
t = ss::to_num<T>(s.c_str(), s.c_str() + s.size());
|
||||||
{ \
|
CHECK_FALSE(t.has_value());
|
||||||
std::string s = std::to_string(std::numeric_limits<type>::min()); \
|
}
|
||||||
auto t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
|
{
|
||||||
CHECK(t.has_value()); \
|
std::string s = std::to_string(numeric_limits<T>::min());
|
||||||
for (auto& i : s) { \
|
auto t = ss::to_num<T>(s.c_str(), s.c_str() + s.size());
|
||||||
if (std::is_signed_v<type> && i != '9' && i != '.') { \
|
CHECK(t.has_value());
|
||||||
i = '9'; \
|
for (auto& i : s) {
|
||||||
break; \
|
if (is_signed<T>::value && i != '9' && i != '.') {
|
||||||
} else if (std::is_unsigned_v<type>) { \
|
i = '9';
|
||||||
s = "-1"; \
|
break;
|
||||||
break; \
|
} else if (is_unsigned<T>::value) {
|
||||||
} \
|
s = "-1";
|
||||||
} \
|
break;
|
||||||
t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
|
}
|
||||||
CHECK_FALSE(t.has_value()); \
|
}
|
||||||
|
t = ss::to_num<T>(s.c_str(), s.c_str() + s.size());
|
||||||
|
CHECK_FALSE(t.has_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("extract test functions for numbers with out of range inputs") {
|
|
||||||
CHECK_OUT_OF_RANGE_CONVERSION(short);
|
|
||||||
CHECK_OUT_OF_RANGE_CONVERSION(us);
|
|
||||||
CHECK_OUT_OF_RANGE_CONVERSION(int);
|
|
||||||
CHECK_OUT_OF_RANGE_CONVERSION(ui);
|
|
||||||
CHECK_OUT_OF_RANGE_CONVERSION(long);
|
|
||||||
CHECK_OUT_OF_RANGE_CONVERSION(ul);
|
|
||||||
CHECK_OUT_OF_RANGE_CONVERSION(ll);
|
|
||||||
CHECK_OUT_OF_RANGE_CONVERSION(ull);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("extract test functions for boolean values") {
|
TEST_CASE("extract test functions for boolean values") {
|
||||||
@@ -142,12 +165,12 @@ TEST_CASE("extract test functions for char values") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("extract test functions for std::optional") {
|
TEST_CASE_TEMPLATE("extract test functions for std::optional", T, int,
|
||||||
for (const auto& [i, s] :
|
ss::int8) {
|
||||||
{std::pair<std::optional<int>, std::string>{1, "1"},
|
for (const auto& [i, s] : {std::pair<std::optional<T>, std::string>{1, "1"},
|
||||||
{69, "69"},
|
{69, "69"},
|
||||||
{-4, "-4"}}) {
|
{-4, "-4"}}) {
|
||||||
std::optional<int> v;
|
std::optional<T> v;
|
||||||
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), v));
|
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), v));
|
||||||
REQUIRE(v.has_value());
|
REQUIRE(v.has_value());
|
||||||
CHECK_EQ(*v, i);
|
CHECK_EQ(*v, i);
|
||||||
@@ -164,7 +187,7 @@ TEST_CASE("extract test functions for std::optional") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const std::string s : {"aa", "xxx", ""}) {
|
for (const std::string s : {"aa", "xxx", ""}) {
|
||||||
std::optional<int> v;
|
std::optional<T> v;
|
||||||
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), v));
|
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), v));
|
||||||
CHECK_FALSE(v.has_value());
|
CHECK_FALSE(v.has_value());
|
||||||
}
|
}
|
||||||
@@ -176,56 +199,57 @@ TEST_CASE("extract test functions for std::optional") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("extract test functions for std::variant") {
|
TEST_CASE_TEMPLATE("extract test functions for std::variant", T, int,
|
||||||
|
ss::uint8) {
|
||||||
{
|
{
|
||||||
std::string s = "22";
|
std::string s = "22";
|
||||||
{
|
{
|
||||||
std::variant<int, double, std::string> var;
|
std::variant<T, double, std::string> var;
|
||||||
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
||||||
CHECK_NOT_VARIANT(var, double);
|
CHECK_NOT_VARIANT(var, double);
|
||||||
CHECK_NOT_VARIANT(var, std::string);
|
CHECK_NOT_VARIANT(var, std::string);
|
||||||
REQUIRE_VARIANT(var, 22, int);
|
REQUIRE_VARIANT(var, 22, T);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::variant<double, int, std::string> var;
|
std::variant<double, T, std::string> var;
|
||||||
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
||||||
CHECK_NOT_VARIANT(var, int);
|
CHECK_NOT_VARIANT(var, T);
|
||||||
CHECK_NOT_VARIANT(var, std::string);
|
CHECK_NOT_VARIANT(var, std::string);
|
||||||
REQUIRE_VARIANT(var, 22, double);
|
REQUIRE_VARIANT(var, 22, double);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::variant<std::string, double, int> var;
|
std::variant<std::string, double, T> var;
|
||||||
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
||||||
CHECK_NOT_VARIANT(var, int);
|
CHECK_NOT_VARIANT(var, T);
|
||||||
CHECK_NOT_VARIANT(var, double);
|
CHECK_NOT_VARIANT(var, double);
|
||||||
REQUIRE_VARIANT(var, "22", std::string);
|
REQUIRE_VARIANT(var, "22", std::string);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::variant<int> var;
|
std::variant<T> var;
|
||||||
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
||||||
REQUIRE_VARIANT(var, 22, int);
|
REQUIRE_VARIANT(var, 22, T);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::string s = "22.2";
|
std::string s = "22.2";
|
||||||
{
|
{
|
||||||
std::variant<int, double, std::string> var;
|
std::variant<T, double, std::string> var;
|
||||||
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
||||||
CHECK_NOT_VARIANT(var, int);
|
CHECK_NOT_VARIANT(var, T);
|
||||||
CHECK_NOT_VARIANT(var, std::string);
|
CHECK_NOT_VARIANT(var, std::string);
|
||||||
REQUIRE_VARIANT(var, 22.2, double);
|
REQUIRE_VARIANT(var, 22.2, double);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::variant<double, int, std::string> var;
|
std::variant<double, T, std::string> var;
|
||||||
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
||||||
CHECK_NOT_VARIANT(var, int);
|
CHECK_NOT_VARIANT(var, T);
|
||||||
CHECK_NOT_VARIANT(var, std::string);
|
CHECK_NOT_VARIANT(var, std::string);
|
||||||
REQUIRE_VARIANT(var, 22.2, double);
|
REQUIRE_VARIANT(var, 22.2, double);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::variant<std::string, double, int> var;
|
std::variant<std::string, double, T> var;
|
||||||
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
||||||
CHECK_NOT_VARIANT(var, int);
|
CHECK_NOT_VARIANT(var, T);
|
||||||
CHECK_NOT_VARIANT(var, double);
|
CHECK_NOT_VARIANT(var, double);
|
||||||
REQUIRE_VARIANT(var, "22.2", std::string);
|
REQUIRE_VARIANT(var, "22.2", std::string);
|
||||||
}
|
}
|
||||||
@@ -233,45 +257,45 @@ TEST_CASE("extract test functions for std::variant") {
|
|||||||
{
|
{
|
||||||
std::string s = "2.2.2";
|
std::string s = "2.2.2";
|
||||||
{
|
{
|
||||||
std::variant<int, double, std::string> var;
|
std::variant<T, double, std::string> var;
|
||||||
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
||||||
CHECK_NOT_VARIANT(var, int);
|
CHECK_NOT_VARIANT(var, T);
|
||||||
CHECK_NOT_VARIANT(var, double);
|
CHECK_NOT_VARIANT(var, double);
|
||||||
REQUIRE_VARIANT(var, "2.2.2", std::string);
|
REQUIRE_VARIANT(var, "2.2.2", std::string);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::variant<double, std::string, int> var;
|
std::variant<double, std::string, T> var;
|
||||||
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
||||||
CHECK_NOT_VARIANT(var, int);
|
CHECK_NOT_VARIANT(var, T);
|
||||||
CHECK_NOT_VARIANT(var, double);
|
CHECK_NOT_VARIANT(var, double);
|
||||||
REQUIRE_VARIANT(var, "2.2.2", std::string);
|
REQUIRE_VARIANT(var, "2.2.2", std::string);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::variant<std::string, double, int> var;
|
std::variant<std::string, double, T> var;
|
||||||
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
||||||
CHECK_NOT_VARIANT(var, int);
|
CHECK_NOT_VARIANT(var, T);
|
||||||
CHECK_NOT_VARIANT(var, double);
|
CHECK_NOT_VARIANT(var, double);
|
||||||
REQUIRE_VARIANT(var, "2.2.2", std::string);
|
REQUIRE_VARIANT(var, "2.2.2", std::string);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::variant<int, double> var;
|
std::variant<T, double> var;
|
||||||
REQUIRE_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
REQUIRE_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
||||||
|
|
||||||
REQUIRE_VARIANT(var, int{}, int);
|
REQUIRE_VARIANT(var, T{}, T);
|
||||||
CHECK_NOT_VARIANT(var, double);
|
CHECK_NOT_VARIANT(var, double);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::variant<double, int> var;
|
std::variant<double, T> var;
|
||||||
REQUIRE_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
REQUIRE_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
||||||
|
|
||||||
REQUIRE_VARIANT(var, double{}, double);
|
REQUIRE_VARIANT(var, double{}, double);
|
||||||
CHECK_NOT_VARIANT(var, int);
|
CHECK_NOT_VARIANT(var, T);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::variant<int> var;
|
std::variant<T> var;
|
||||||
REQUIRE_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
REQUIRE_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
|
||||||
|
|
||||||
REQUIRE_VARIANT(var, int{}, int);
|
REQUIRE_VARIANT(var, T{}, T);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,12 +62,12 @@ struct buffer {
|
|||||||
[[maybe_unused]] inline buffer buff;
|
[[maybe_unused]] inline buffer buff;
|
||||||
|
|
||||||
[[maybe_unused]] std::string time_now_rand() {
|
[[maybe_unused]] std::string time_now_rand() {
|
||||||
srand(time(nullptr));
|
std::srand(std::time(nullptr));
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
auto t = std::time(nullptr);
|
auto t = std::time(nullptr);
|
||||||
auto tm = *std::localtime(&t);
|
auto tm = *std::localtime(&t);
|
||||||
ss << std::put_time(&tm, "%d%m%Y%H%M%S");
|
ss << std::put_time(&tm, "%d%m%Y%H%M%S");
|
||||||
srand(time(nullptr));
|
std::srand(std::time(nullptr));
|
||||||
return ss.str() + std::to_string(rand());
|
return ss.str() + std::to_string(rand());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,8 +78,8 @@ struct unique_file_name {
|
|||||||
|
|
||||||
unique_file_name(const std::string& test) {
|
unique_file_name(const std::string& test) {
|
||||||
do {
|
do {
|
||||||
name = "ssp_test_" + test + "_" + std::to_string(i++) +
|
name = "ssp_test_" + test + "_" + std::to_string(i++) + "_" +
|
||||||
"_" + time_now_rand() + "_file.csv";
|
time_now_rand() + "_file.csv";
|
||||||
} while (std::filesystem::exists(name));
|
} while (std::filesystem::exists(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,9 +77,9 @@ TEST_CASE_TEMPLATE("test position method", T, ParserOptionCombinations) {
|
|||||||
if (!buff.empty()) {
|
if (!buff.empty()) {
|
||||||
return buff[n];
|
return buff[n];
|
||||||
} else {
|
} else {
|
||||||
auto file = fopen(f.name.c_str(), "r");
|
auto file = std::fopen(f.name.c_str(), "r");
|
||||||
fseek(file, n, SEEK_SET);
|
std::fseek(file, n, SEEK_SET);
|
||||||
return static_cast<char>(fgetc(file));
|
return static_cast<char>(std::fgetc(file));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user