mirror of
https://github.com/red0124/ssp.git
synced 2025-01-23 13:05:20 +01:00
update documentation, fix minor bug
This commit is contained in:
parent
aa97b18aa7
commit
aec49e7e4f
96
README.md
96
README.md
@ -85,30 +85,92 @@ the **set_error_mode** method:
|
|||||||
void parser::set_error_mode(ss::error_mode);
|
void parser::set_error_mode(ss::error_mode);
|
||||||
const std::string& parser::error_msg();
|
const std::string& parser::error_msg();
|
||||||
bool parser::valid();
|
bool parser::valid();
|
||||||
|
bool parser::eof();
|
||||||
```
|
```
|
||||||
Error messages can always be disabled by setting the error mode to
|
Error messages can always be disabled by setting the error mode to
|
||||||
**error_mode::Bool**. An error can be detected using the **valid** method which
|
**error_mode::Bool**. An error can be detected using the **valid** method which
|
||||||
will return **false** if the file could not be opened, or if the conversion
|
would return **false** if the file could not be opened, or if the conversion
|
||||||
could not be made (invalid types, invalid number of columns, ...).
|
could not be made (invalid types, invalid number of columns, ...).
|
||||||
|
The **eof** method can be used to detect if the end of the file was reached.
|
||||||
|
|
||||||
## Conversions
|
## Conversions
|
||||||
The above example will be used to show some of the features of the library.
|
The above example will be used to show some of the features of the library.
|
||||||
As seen above, the **get_next** method returns a tuple of objects specified
|
As seen above, the **get_next** method returns a tuple of objects specified
|
||||||
inside the template type list.
|
inside the template type list.
|
||||||
|
|
||||||
We could pass **void** as the second template parameter to ignore the second (age)
|
If a conversion could not be applied, the method would return a tuple of
|
||||||
column in our csv, a tuple of only 2 parameters will be retuned:
|
default constructed objects, and **valid** would return **false**, for example
|
||||||
|
if the third (grade) column in our csv could not be converted to a double
|
||||||
|
the conversion would fail.
|
||||||
|
|
||||||
|
If **get_next** is called with a **tuple** it would behave identically to passing
|
||||||
|
the same tuple parameters to **get_next**:
|
||||||
|
```cpp
|
||||||
|
using student = std::tuple<std::string, int, double>;
|
||||||
|
|
||||||
|
// returns std::tuple<std::string, int, double>
|
||||||
|
auto [name, age, grade] = p.get_next<student>();
|
||||||
|
```
|
||||||
|
*Note, it does not always return a student tuple since the returned tuples
|
||||||
|
parameters may be altered as explained below (no void, no restrictions, ...)*
|
||||||
|
|
||||||
|
Whole objects can be returned using the **get_object** function which takes the
|
||||||
|
tuple, created in a similar way as **get_next** does it, and creates an object
|
||||||
|
out of it:
|
||||||
|
```cpp
|
||||||
|
struct student {
|
||||||
|
std::string name;
|
||||||
|
int age;
|
||||||
|
double grade;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
```cpp
|
||||||
|
// returns student
|
||||||
|
auto student = p.get_object<student, std::string, int, double>();
|
||||||
|
```
|
||||||
|
This works with any object if the constructor could be invoked using the
|
||||||
|
template arguments given to **get_object**:
|
||||||
|
```cpp
|
||||||
|
// returns std::vector<std::string> containing 3 elements
|
||||||
|
auto vec = p.get_object<std::vector<std::string>, std::string, std::string,
|
||||||
|
std::string>();
|
||||||
|
```
|
||||||
|
And finally, using something I personally like to do, a struct (class) with a **tied**
|
||||||
|
method witch returns a tuple of references to to the members of the struct.
|
||||||
|
```cpp
|
||||||
|
struct student {
|
||||||
|
std::string name;
|
||||||
|
int age;
|
||||||
|
double grade;
|
||||||
|
|
||||||
|
auto tied() { return std::tie(name, 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.
|
||||||
|
```cpp
|
||||||
|
// returns student
|
||||||
|
auto s = p.get_next<student>();
|
||||||
|
```
|
||||||
|
*Note, the order in which the members of the tied method are returned must
|
||||||
|
match the order of the elements in the csv*
|
||||||
|
|
||||||
|
### Special types
|
||||||
|
|
||||||
|
Passing **void** makes the parser ignore a column.
|
||||||
|
In the given 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, double>
|
// returns std::tuple<std::string, double>
|
||||||
auto [name, grade] = p.get_next<std::string, void, double>();
|
auto [name, grade] = p.get_next<std::string, void, double>();
|
||||||
```
|
```
|
||||||
|
To ignore a whole row, **ignore_next** could be used, returns **false** if **eof**:
|
||||||
If a conversion could not be applied, the method would return a tuple of
|
```cpp
|
||||||
default constructed objects, and **valid** would return **false** , for example
|
bool parser::ignore_next();
|
||||||
if the third (grade) column in our csv could not be converted to a double
|
```
|
||||||
the conversion would fail. **std::optional<double>** could be passed if we
|
**std::optional** could be passed if we wanted the conversion to proceed in the
|
||||||
wanted the conversion to proceed in the case of a failure:
|
case of a failure returning **std::nullopt** for the specified column:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// returns std::tuple<std::string, int, std::optional<double>>
|
// returns std::tuple<std::string, int, std::optional<double>>
|
||||||
@ -117,11 +179,9 @@ if(grade) {
|
|||||||
// do something with grade
|
// do something with grade
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Similar to **std::optional**, **std::variant** could be used to try other
|
Similar to **std::optional**, **std::variant** could be used to try other
|
||||||
conversions if the previous failed _(note: conversion to **std::string** will
|
conversions if the previous failed _(Note, conversion to std::string will
|
||||||
always pass)_:
|
always pass)_:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// returns std::tuple<std::string, int, std::variant<double, char>>
|
// returns std::tuple<std::string, int, std::variant<double, char>>
|
||||||
auto [name, age, grade] =
|
auto [name, age, grade] =
|
||||||
@ -132,24 +192,20 @@ if(std::holds_alternative<double>(grade)) {
|
|||||||
// grade set as char
|
// grade set as char
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
### Restrictions
|
||||||
## Restrictions
|
|
||||||
|
|
||||||
Custom **restrictions** can be used to narrow down the conversions of unwanted
|
Custom **restrictions** can be used to narrow down the conversions of unwanted
|
||||||
values. **ss::ir** (in range) and **ss::ne** (none empty) are one of those:
|
values. **ss::ir** (in range) and **ss::ne** (none empty) are one of those:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// returns std::tuple<std::string, int, double>
|
|
||||||
// ss::ne makes sure that the name is not empty
|
// ss::ne makes sure that the name is not empty
|
||||||
// ss::ir makes sure that the grade will be in range [0, 10]
|
// ss::ir makes sure that the grade will be in range [0, 10]
|
||||||
|
// returns std::tuple<std::string, int, double>
|
||||||
auto [name, age, grade] =
|
auto [name, age, grade] =
|
||||||
p.get_next<ss::ne<std::string>, int, ss::ir<double, 0, 10>>();
|
p.get_next<ss::ne<std::string>, int, ss::ir<double, 0, 10>>();
|
||||||
```
|
```
|
||||||
|
|
||||||
If the restrictions are not met, the conversion will fail.
|
If the restrictions are not met, the conversion will fail.
|
||||||
Other predefined restrictions are **ss::ax** (all except), **ss::nx** (none except)
|
Other predefined restrictions are **ss::ax** (all except), **ss::nx** (none except)
|
||||||
and **ss::oor** (out of range):
|
and **ss::oor** (out of range):
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// all ints exept 10 and 20
|
// all ints exept 10 and 20
|
||||||
ss::ax<int, 10, 20>
|
ss::ax<int, 10, 20>
|
||||||
@ -158,13 +214,11 @@ ss::nx<int, 10, 20>
|
|||||||
// all values except the range [0, 10]
|
// all values except the range [0, 10]
|
||||||
ss::oor<int, 0, 10>
|
ss::oor<int, 0, 10>
|
||||||
```
|
```
|
||||||
|
|
||||||
To define a restriction, a class/struct needs to be made which has a
|
To define a restriction, a class/struct needs to be made which has a
|
||||||
**ss_valid** method which returns a **bool** and accepts one object. The type of the
|
**ss_valid** method which returns a **bool** and accepts one object. The type of the
|
||||||
conversion will be the same as the type of the passed object within **ss_valid**
|
conversion will be the same as the type of the passed object within **ss_valid**
|
||||||
and not the restriction itself. Optionally, an **error** method can be made to
|
and not the restriction itself. Optionally, an **error** method can be made to
|
||||||
describe the invalid conversion.
|
describe the invalid conversion.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct even {
|
struct even {
|
||||||
@ -172,6 +226,7 @@ struct even {
|
|||||||
return value % 2 == 0;
|
return value % 2 == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// optional
|
||||||
const char* error() const {
|
const char* error() const {
|
||||||
return "number not even";
|
return "number not even";
|
||||||
}
|
}
|
||||||
@ -182,4 +237,3 @@ struct even {
|
|||||||
// returns std::tuple<std::string, int>
|
// returns std::tuple<std::string, int>
|
||||||
auto [name, age] = p.get_next<std::string, even<int>, void>();
|
auto [name, age] = p.get_next<std::string, even<int>, void>();
|
||||||
|
|
||||||
```
|
|
||||||
|
@ -145,7 +145,11 @@ public:
|
|||||||
// method which returns a tuple, returns that type
|
// method which returns a tuple, returns that type
|
||||||
template <typename T, typename... Ts>
|
template <typename T, typename... Ts>
|
||||||
no_void_validator_tup_t<T, Ts...> convert(const split_input& elems) {
|
no_void_validator_tup_t<T, Ts...> convert(const split_input& elems) {
|
||||||
if constexpr (tied_class_v<T, Ts...>) {
|
if constexpr (sizeof...(Ts) == 0 &&
|
||||||
|
is_instance_of<T, std::tuple>::value) {
|
||||||
|
return convert_impl(elems, (T*){});
|
||||||
|
|
||||||
|
} else if constexpr (tied_class_v<T, Ts...>) {
|
||||||
using arg_ref_tuple =
|
using arg_ref_tuple =
|
||||||
typename std::result_of_t<decltype (&T::tied)(T)>;
|
typename std::result_of_t<decltype (&T::tied)(T)>;
|
||||||
|
|
||||||
@ -153,10 +157,6 @@ public:
|
|||||||
typename apply_trait<std::decay, arg_ref_tuple>::type;
|
typename apply_trait<std::decay, arg_ref_tuple>::type;
|
||||||
|
|
||||||
return to_object<T>(convert_impl(elems, (arg_tuple*){}));
|
return to_object<T>(convert_impl(elems, (arg_tuple*){}));
|
||||||
} else if constexpr (sizeof...(Ts) == 0 &&
|
|
||||||
is_instance_of<T, std::tuple>::value) {
|
|
||||||
return convert_impl(elems, (T*){});
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return convert_impl<T, Ts...>(elems);
|
return convert_impl<T, Ts...>(elems);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user