mirror of
https://github.com/red0124/ssp.git
synced 2025-01-23 13:05:20 +01:00
add lt gt lte gte restrictions, update unit tests, update documentation
This commit is contained in:
parent
cc7e6f7806
commit
fdae9b6413
87
README.md
87
README.md
@ -76,23 +76,6 @@ $ make test
|
|||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
## Error handling
|
|
||||||
|
|
||||||
Detailed error messages can be accessed via the **error_msg** method, and to
|
|
||||||
enable them the error mode has to be changed to **error_mode::error_string** using
|
|
||||||
the **set_error_mode** method:
|
|
||||||
```cpp
|
|
||||||
void parser::set_error_mode(ss::error_mode);
|
|
||||||
const std::string& parser::error_msg();
|
|
||||||
bool parser::valid();
|
|
||||||
bool parser::eof();
|
|
||||||
```
|
|
||||||
Error messages can always be disabled by setting the error mode to
|
|
||||||
**error_mode::error_bool**. An error can be detected using the **valid** method which
|
|
||||||
would return **false** if the file could not be opened, or if the conversion
|
|
||||||
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
|
||||||
@ -212,7 +195,7 @@ auto [name, age, grade] =
|
|||||||
```
|
```
|
||||||
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), ss::lt (less than), ...(see *restrictions.hpp*):
|
||||||
```cpp
|
```cpp
|
||||||
// all ints exept 10 and 20
|
// all ints exept 10 and 20
|
||||||
ss::ax<int, 10, 20>
|
ss::ax<int, 10, 20>
|
||||||
@ -246,7 +229,7 @@ auto [name, age] = p.get_next<std::string, even<int>, void>();
|
|||||||
```
|
```
|
||||||
## Custom conversions
|
## Custom conversions
|
||||||
|
|
||||||
Custom types can be used when converting values. An override of the **ss::extract**
|
Custom types can be used when converting values. A specialization of the **ss::extract**
|
||||||
function needs to be made and you are good to go. Custom conversion for an enum
|
function needs to be made and you are good to go. Custom conversion for an enum
|
||||||
would look like this:
|
would look like this:
|
||||||
```cpp
|
```cpp
|
||||||
@ -267,11 +250,28 @@ inline bool ss::extract(const char* begin, const char* end, shape& dst) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
The shape enum will be in an example below. The **inline** is there just to prevent
|
The shape enum will be used in an example below. The **inline** is there just to prevent
|
||||||
multiple definition errors. The function returns **true** if the conversion was
|
multiple definition errors. The function returns **true** if the conversion was
|
||||||
a success, and **false** otherwise. The function uses **const char*** begin and end
|
a success, and **false** otherwise. The function uses **const char*** begin and end
|
||||||
for performance reasons.
|
for performance reasons.
|
||||||
|
|
||||||
|
## Error handling
|
||||||
|
|
||||||
|
Detailed error messages can be accessed via the **error_msg** method, and to
|
||||||
|
enable them the error mode has to be changed to **error_mode::error_string** using
|
||||||
|
the **set_error_mode** method:
|
||||||
|
```cpp
|
||||||
|
void parser::set_error_mode(ss::error_mode);
|
||||||
|
const std::string& parser::error_msg();
|
||||||
|
bool parser::valid();
|
||||||
|
bool parser::eof();
|
||||||
|
```
|
||||||
|
Error messages can always be disabled by setting the error mode to
|
||||||
|
**error_mode::error_bool**. An error can be detected using the **valid** method which
|
||||||
|
would return **false** if the file could not be opened, or if the conversion
|
||||||
|
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.
|
||||||
|
|
||||||
## Substitute conversions
|
## Substitute conversions
|
||||||
|
|
||||||
The parser can also be used to effectively parse files whose rows are not
|
The parser can also be used to effectively parse files whose rows are not
|
||||||
@ -298,11 +298,12 @@ if (!p.valid()) {
|
|||||||
std::vector<std::pair<shape, double>> shapes;
|
std::vector<std::pair<shape, double>> shapes;
|
||||||
|
|
||||||
while (!p.eof()) {
|
while (!p.eof()) {
|
||||||
using ss::nx;
|
// non negative double
|
||||||
|
using udbl = ss::gte<double, 0>;
|
||||||
auto [circle_or_square, rectangle, triangle] =
|
auto [circle_or_square, rectangle, triangle] =
|
||||||
p.try_next<nx<shape, shape::circle, shape::square>, double>()
|
p.try_next<ss::nx<shape, shape::circle, shape::square>, udbl>()
|
||||||
.or_else<nx<shape, shape::rectangle>, double, double>()
|
.or_else<ss::nx<shape, shape::rectangle>, udbl, udbl>()
|
||||||
.or_else<nx<shape, shape::triangle>, double, double, double>()
|
.or_else<ss::nx<shape, shape::triangle>, udbl, udbl, udbl>()
|
||||||
.values();
|
.values();
|
||||||
|
|
||||||
if (circle_or_square) {
|
if (circle_or_square) {
|
||||||
@ -319,10 +320,46 @@ while (!p.eof()) {
|
|||||||
if (triangle) {
|
if (triangle) {
|
||||||
auto& [s, a, b, c] = triangle.value();
|
auto& [s, a, b, c] = triangle.value();
|
||||||
double sh = (a + b + c) / 2;
|
double sh = (a + b + c) / 2;
|
||||||
shapes.emplace_back(s, sqrt(sh * (sh - a) * (sh - b) * (sh - c)));
|
if (sh >= a && sh >= b && sh >= c) {
|
||||||
|
double area = sqrt(sh * (sh - a) * (sh - b) * (sh - c));
|
||||||
|
shapes.emplace_back(s, area);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* do something with the stored shapes */
|
/* do something with the stored shapes */
|
||||||
/* ... */
|
/* ... */
|
||||||
```
|
```
|
||||||
|
It is quite hard to make an error this way since most things will be checked
|
||||||
|
at compile time.
|
||||||
|
|
||||||
|
The **try_next** method works in a similar way as **get_next** but returns a **composit**
|
||||||
|
which holds a **tuple** with an **optional** to the **tuple** returned by **get_next**.
|
||||||
|
This **composite** has a **or_else** method (looks a bit like tl::expected) which
|
||||||
|
is able to try additional conversions if the previous failed.
|
||||||
|
It also returns a **composite**, but in its tuple is the **optional** to the **tuple**
|
||||||
|
of the previous conversions and an **optional** to the **tuple** to the new conversion.
|
||||||
|
|
||||||
|
To fetch the **tuple** from the **composite** the **values** method is used.
|
||||||
|
The value of the above used conversion would look something like this
|
||||||
|
(with the restrictions applied to the values of shape - ss::nx)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::tuple<
|
||||||
|
std::optional<std::tuple<shape, double>>,
|
||||||
|
std::optional<std::tuple<shape, double, double>>,
|
||||||
|
std::optional<std::tuple<shape, double, double, double>>
|
||||||
|
>
|
||||||
|
```
|
||||||
|
|
||||||
|
Similar to the way that **get_next** has a **get_object** alternative, **try_next** has a **try_object**
|
||||||
|
alternative, and **or_else** has a **or_object** alternative. Also all rules applied
|
||||||
|
to **get_next** also work with **try_next** , **or_else**, and all the other **composite** conversions.
|
||||||
|
|
||||||
|
Each of those **composite** conversions can accept a lambda (or anything callable) as
|
||||||
|
an argument and invoke it in case of a valid conversion. That lambda
|
||||||
|
itself need not have any arguments, but if they do, they must either
|
||||||
|
accept the whole **tuple**/object as one argument or the elements of the tuple
|
||||||
|
separately. If the lambda returns something that can be interpreted as **false**,
|
||||||
|
The conversion will fail, and the next conversion will try to apply.
|
||||||
|
Rewriting the whole while loop using lambdas would look like this:
|
||||||
|
@ -52,6 +52,41 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
////////////////
|
||||||
|
// greater than or equal to
|
||||||
|
// greater than
|
||||||
|
// less than
|
||||||
|
// less than or equal to
|
||||||
|
////////////////
|
||||||
|
|
||||||
|
template <typename T, auto N>
|
||||||
|
struct gt {
|
||||||
|
bool ss_valid(const T& value) const {
|
||||||
|
return value > N;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, auto N>
|
||||||
|
struct gte {
|
||||||
|
bool ss_valid(const T& value) const {
|
||||||
|
return value >= N;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, auto N>
|
||||||
|
struct lt {
|
||||||
|
bool ss_valid(const T& value) const {
|
||||||
|
return value < N;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, auto N>
|
||||||
|
struct lte {
|
||||||
|
bool ss_valid(const T& value) const {
|
||||||
|
return value <= N;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
////////////////
|
////////////////
|
||||||
// in range
|
// in range
|
||||||
////////////////
|
////////////////
|
||||||
@ -61,10 +96,6 @@ struct ir {
|
|||||||
bool ss_valid(const T& value) const {
|
bool ss_valid(const T& value) const {
|
||||||
return value >= Min && value <= Max;
|
return value >= Min && value <= Max;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* error() const {
|
|
||||||
return "out of range";
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////
|
////////////////
|
||||||
@ -76,10 +107,6 @@ struct oor {
|
|||||||
bool ss_valid(const T& value) const {
|
bool ss_valid(const T& value) const {
|
||||||
return value < Min || value > Max;
|
return value < Min || value > Max;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* error() const {
|
|
||||||
return "in restricted range";
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////
|
////////////////
|
||||||
|
@ -323,6 +323,64 @@ TEST_CASE("testing ss:ne restriction (not empty)") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("testing ss:lt ss::lte ss::gt ss::gte restriction (in range)") {
|
||||||
|
ss::converter c;
|
||||||
|
|
||||||
|
c.convert<ss::lt<int, 3>>("3");
|
||||||
|
REQUIRE(!c.valid());
|
||||||
|
|
||||||
|
c.convert<ss::lt<int, 2>>("3");
|
||||||
|
REQUIRE(!c.valid());
|
||||||
|
|
||||||
|
c.convert<ss::gt<int, 3>>("3");
|
||||||
|
REQUIRE(!c.valid());
|
||||||
|
|
||||||
|
c.convert<ss::gt<int, 4>>("3");
|
||||||
|
REQUIRE(!c.valid());
|
||||||
|
|
||||||
|
c.convert<ss::lte<int, 2>>("3");
|
||||||
|
REQUIRE(!c.valid());
|
||||||
|
|
||||||
|
c.convert<ss::gte<int, 4>>("3");
|
||||||
|
REQUIRE(!c.valid());
|
||||||
|
|
||||||
|
{
|
||||||
|
auto tup = c.convert<ss::lt<int, 4>>("3");
|
||||||
|
REQUIRE(c.valid());
|
||||||
|
CHECK(tup == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto tup = c.convert<ss::gt<int, 2>>("3");
|
||||||
|
REQUIRE(c.valid());
|
||||||
|
CHECK(tup == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto tup = c.convert<ss::lte<int, 4>>("3");
|
||||||
|
REQUIRE(c.valid());
|
||||||
|
CHECK(tup == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto tup = c.convert<ss::lte<int, 3>>("3");
|
||||||
|
REQUIRE(c.valid());
|
||||||
|
CHECK(tup == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto tup = c.convert<ss::gte<int, 2>>("3");
|
||||||
|
REQUIRE(c.valid());
|
||||||
|
CHECK(tup == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto tup = c.convert<ss::gte<int, 3>>("3");
|
||||||
|
REQUIRE(c.valid());
|
||||||
|
CHECK(tup == 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("testing error mode") {
|
TEST_CASE("testing error mode") {
|
||||||
ss::converter c;
|
ss::converter c;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user