Commit d621658e6dcd8080e5630252ac2d02528f6b0cfc
Committed by
Henry Schreiner
1 parent
51a395ec
Issue #339 (#348)
* add an example where the name of the enum is printed through a stream output function, which subverted the checkTransformer conversion and prevented conversion of the enumeration. * add missing 'typename' * try a simpler version of the value_string using const reference for all overloads * use auto return type to match to_string return type in value_string * remove extra spaces
Showing
4 changed files
with
66 additions
and
4 deletions
examples/CMakeLists.txt
| ... | ... | @@ -203,6 +203,11 @@ add_test(NAME enum_fail COMMAND enum -l 4) |
| 203 | 203 | set_property(TEST enum_fail PROPERTY PASS_REGULAR_EXPRESSION |
| 204 | 204 | "--level: Check 4 value in {" "FAILED") |
| 205 | 205 | |
| 206 | +add_cli_exe(enum_ostream enum_ostream.cpp) | |
| 207 | +add_test(NAME enum_ostream_pass COMMAND enum_ostream --level medium) | |
| 208 | +set_property(TEST enum_ostream_pass PROPERTY PASS_REGULAR_EXPRESSION | |
| 209 | + "Enum received: Medium") | |
| 210 | + | |
| 206 | 211 | add_cli_exe(digit_args digit_args.cpp) |
| 207 | 212 | add_test(NAME digit_args COMMAND digit_args -h) |
| 208 | 213 | set_property(TEST digit_args PROPERTY PASS_REGULAR_EXPRESSION | ... | ... |
examples/enum_ostream.cpp
0 → 100644
| 1 | +#include <CLI/CLI.hpp> | |
| 2 | + | |
| 3 | +enum class Level : int { High, Medium, Low }; | |
| 4 | + | |
| 5 | +// this breaks the code | |
| 6 | +inline std::ostream &operator<<(std::ostream &o, const Level &l) { | |
| 7 | + switch(l) { | |
| 8 | + case Level::High: | |
| 9 | + o << "High"; | |
| 10 | + break; | |
| 11 | + case Level::Medium: | |
| 12 | + o << "Medium"; | |
| 13 | + break; | |
| 14 | + case Level::Low: | |
| 15 | + o << "Low"; | |
| 16 | + break; | |
| 17 | + } | |
| 18 | + return o; | |
| 19 | +} | |
| 20 | + | |
| 21 | +int main(int argc, char **argv) { | |
| 22 | + CLI::App app; | |
| 23 | + | |
| 24 | + Level level; | |
| 25 | + // specify string->value mappings | |
| 26 | + std::vector<std::pair<std::string, Level>> map{ | |
| 27 | + {"high", Level::High}, {"medium", Level::Medium}, {"low", Level::Low}}; | |
| 28 | + // checked Transform does the translation and checks the results are either in one of the strings or one of the | |
| 29 | + // translations already | |
| 30 | + app.add_option("-l,--level", level, "Level settings") | |
| 31 | + ->required() | |
| 32 | + ->transform(CLI::CheckedTransformer(map, CLI::ignore_case)); | |
| 33 | + | |
| 34 | + CLI11_PARSE(app, argc, argv); | |
| 35 | + | |
| 36 | + // CLI11's built in enum streaming can be used outside CLI11 like this: | |
| 37 | + using namespace CLI::enums; | |
| 38 | + std::cout << "Enum received: " << level << std::endl; | |
| 39 | + | |
| 40 | + return 0; | |
| 41 | +} | ... | ... |
include/CLI/TypeTools.hpp
| ... | ... | @@ -259,6 +259,22 @@ template <typename T1, |
| 259 | 259 | std::string checked_to_string(T &&) { |
| 260 | 260 | return std::string{}; |
| 261 | 261 | } |
| 262 | +/// get a string as a convertible value for arithmetic types | |
| 263 | +template <typename T, enable_if_t<std::is_arithmetic<T>::value, detail::enabler> = detail::dummy> | |
| 264 | +std::string value_string(const T &value) { | |
| 265 | + return std::to_string(value); | |
| 266 | +} | |
| 267 | +/// get a string as a convertible value for enumerations | |
| 268 | +template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy> | |
| 269 | +std::string value_string(const T &value) { | |
| 270 | + return std::to_string(static_cast<typename std::underlying_type<T>::type>(value)); | |
| 271 | +} | |
| 272 | +/// for other types just use the regular to_string function | |
| 273 | +template <typename T, | |
| 274 | + enable_if_t<!std::is_enum<T>::value && !std::is_arithmetic<T>::value, detail::enabler> = detail::dummy> | |
| 275 | +auto value_string(const T &value) -> decltype(to_string(value)) { | |
| 276 | + return to_string(value); | |
| 277 | +} | |
| 262 | 278 | |
| 263 | 279 | /// This will only trigger for actual void type |
| 264 | 280 | template <typename T, typename Enable = void> struct type_count { static const int value{0}; }; | ... | ... |
include/CLI/Validators.hpp
| ... | ... | @@ -709,7 +709,7 @@ class IsMember : public Validator { |
| 709 | 709 | if(res.first) { |
| 710 | 710 | // Make sure the version in the input string is identical to the one in the set |
| 711 | 711 | if(filter_fn) { |
| 712 | - input = detail::to_string(detail::pair_adaptor<element_t>::first(*(res.second))); | |
| 712 | + input = detail::value_string(detail::pair_adaptor<element_t>::first(*(res.second))); | |
| 713 | 713 | } |
| 714 | 714 | |
| 715 | 715 | // Return empty error string (success) |
| ... | ... | @@ -778,7 +778,7 @@ class Transformer : public Validator { |
| 778 | 778 | } |
| 779 | 779 | auto res = detail::search(mapping, b, filter_fn); |
| 780 | 780 | if(res.first) { |
| 781 | - input = detail::to_string(detail::pair_adaptor<element_t>::second(*res.second)); | |
| 781 | + input = detail::value_string(detail::pair_adaptor<element_t>::second(*res.second)); | |
| 782 | 782 | } |
| 783 | 783 | return std::string{}; |
| 784 | 784 | }; |
| ... | ... | @@ -846,12 +846,12 @@ class CheckedTransformer : public Validator { |
| 846 | 846 | } |
| 847 | 847 | auto res = detail::search(mapping, b, filter_fn); |
| 848 | 848 | if(res.first) { |
| 849 | - input = detail::to_string(detail::pair_adaptor<element_t>::second(*res.second)); | |
| 849 | + input = detail::value_string(detail::pair_adaptor<element_t>::second(*res.second)); | |
| 850 | 850 | return std::string{}; |
| 851 | 851 | } |
| 852 | 852 | } |
| 853 | 853 | for(const auto &v : detail::smart_deref(mapping)) { |
| 854 | - auto output_string = detail::to_string(detail::pair_adaptor<element_t>::second(v)); | |
| 854 | + auto output_string = detail::value_string(detail::pair_adaptor<element_t>::second(v)); | |
| 855 | 855 | if(output_string == input) { |
| 856 | 856 | return std::string(); |
| 857 | 857 | } | ... | ... |