diff --git a/README.md b/README.md index 6d513c9..5fe8b7d 100644 --- a/README.md +++ b/README.md @@ -85,30 +85,92 @@ the **set_error_mode** method: 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::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 -could not be made (invalid types, invalid number of columns, ...). +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 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 inside the template type list. -We could pass **void** as the second template parameter to ignore the second (age) -column in our csv, a tuple of only 2 parameters will be retuned: +If a conversion could not be applied, the method would return a tuple of +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; + +// returns std::tuple +auto [name, age, grade] = p.get_next(); +``` +*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(); +``` +This works with any object if the constructor could be invoked using the +template arguments given to **get_object**: +```cpp +// returns std::vector containing 3 elements +auto vec = p.get_object, 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(); +``` +*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 // returns std::tuple auto [name, grade] = p.get_next(); ``` - -If a conversion could not be applied, the method would return a tuple of -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. **std::optional** could be passed if we -wanted the conversion to proceed in the case of a failure: +To ignore a whole row, **ignore_next** could be used, returns **false** if **eof**: +```cpp +bool parser::ignore_next(); +``` +**std::optional** could be passed if we wanted the conversion to proceed in the +case of a failure returning **std::nullopt** for the specified column: ```cpp // returns std::tuple> @@ -117,11 +179,9 @@ if(grade) { // do something with grade } ``` - 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)_: - ```cpp // returns std::tuple> auto [name, age, grade] = @@ -132,24 +192,20 @@ if(std::holds_alternative(grade)) { // grade set as char } ``` - -## 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 one of those: - ```cpp -// returns std::tuple // ss::ne makes sure that the name is not empty // ss::ir makes sure that the grade will be in range [0, 10] +// returns std::tuple auto [name, age, grade] = p.get_next, int, ss::ir>(); ``` - If the restrictions are not met, the conversion will fail. Other predefined restrictions are **ss::ax** (all except), **ss::nx** (none except) and **ss::oor** (out of range): - ```cpp // all ints exept 10 and 20 ss::ax @@ -158,13 +214,11 @@ ss::nx // all values except the range [0, 10] ss::oor ``` - 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 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 describe the invalid conversion. - ```cpp template struct even { @@ -172,6 +226,7 @@ struct even { return value % 2 == 0; } + // optional const char* error() const { return "number not even"; } @@ -182,4 +237,3 @@ struct even { // returns std::tuple auto [name, age] = p.get_next, void>(); -``` diff --git a/include/ss/converter.hpp b/include/ss/converter.hpp index 51b1004..41a83ee 100644 --- a/include/ss/converter.hpp +++ b/include/ss/converter.hpp @@ -145,7 +145,11 @@ public: // method which returns a tuple, returns that type template no_void_validator_tup_t convert(const split_input& elems) { - if constexpr (tied_class_v) { + if constexpr (sizeof...(Ts) == 0 && + is_instance_of::value) { + return convert_impl(elems, (T*){}); + + } else if constexpr (tied_class_v) { using arg_ref_tuple = typename std::result_of_t; @@ -153,10 +157,6 @@ public: typename apply_trait::type; return to_object(convert_impl(elems, (arg_tuple*){})); - } else if constexpr (sizeof...(Ts) == 0 && - is_instance_of::value) { - return convert_impl(elems, (T*){}); - } else { return convert_impl(elems); }