From ed655e23292953fe76deb100ecc8d556ffcd4a48 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Tue, 8 Jun 2021 22:08:56 +0200 Subject: [PATCH 1/3] Add program_options example with a generic custom validator for enums --- example/program_options.cpp | 112 ++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 example/program_options.cpp diff --git a/example/program_options.cpp b/example/program_options.cpp new file mode 100644 index 0000000..7a2fe51 --- /dev/null +++ b/example/program_options.cpp @@ -0,0 +1,112 @@ + +// Copyright 2021 Samuel Debionne +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +// g++ -std=c++14 -Iinclude example/program_options.cpp -o example/program_options /usr/lib/libboost_program_options.a + +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include + +#include +#include + +namespace app +{ + +enum class log_level { + fatal, + error, + warning, + trace +}; + +BOOST_DESCRIBE_ENUM(log_level, fatal, error, warning, trace) + +template ::value>> +std::ostream& operator<<(std::ostream& os, E e) +{ + boost::mp11::mp_for_each< boost::describe::describe_enumerators >([&](auto D) { + if( e == D.value ) os << D.name; + }); + + return os; +} + +// Generic program_options custom validator for (described) enums +template < + typename E, + typename Ed = boost::describe::describe_enumerators, + typename = std::enable_if_t< + std::is_enum::value && !boost::mp11::mp_empty::value + > +> +void validate(boost::any& v, + const std::vector& values, + E* target_type, int) +{ + using namespace boost::program_options; + + // Make sure no previous assignment to 'a' was made. + validators::check_first_occurrence(v); + // Extract the first string from 'values'. If there is more than + // one string, it's an error, and exception will be thrown. + const std::string& name = validators::get_single_string(values); + + // Convert string to enum + bool found = false; + E r = {}; + + boost::mp11::mp_for_each([&](auto D){ + if( !found && std::strcmp( D.name, name.c_str() ) == 0 ) + { + found = true; + r = D.value; + } + }); + + if (found) + v = boost::any(r); + else + throw validation_error(validation_error::invalid_option_value); +} + +} //namespace app + +int main(int argc, char *argv[]) +{ + namespace po = boost::program_options; + + app::log_level lvl; + + // Declare the supported options. + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "produce help message") + ("log-level", po::value(&lvl), "set log severity level") + ; + + try { + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + if (vm.count("help")) { + std::cout << desc << "\n"; + return 1; + } + + if (vm.count("log-level")) { + std::cout << "Log level was set to " << lvl << ".\n"; + } else { + std::cout << "Log level was not set.\n"; + } + } + catch (std::exception& ex) + { + std::cerr << "ERROR: " << ex.what() << std::endl; + } +} From 65da24f19bd85c796d8f965c72eff59612bc262b Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Mon, 7 Mar 2022 16:33:38 +0100 Subject: [PATCH 2/3] fixup! Add program_options example with a generic custom validator for enums --- example/program_options.cpp | 20 ++------------------ include/boost/describe/operators.hpp | 10 ++++++++++ 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/example/program_options.cpp b/example/program_options.cpp index 7a2fe51..c0b24a9 100644 --- a/example/program_options.cpp +++ b/example/program_options.cpp @@ -26,15 +26,7 @@ enum class log_level { BOOST_DESCRIBE_ENUM(log_level, fatal, error, warning, trace) -template ::value>> -std::ostream& operator<<(std::ostream& os, E e) -{ - boost::mp11::mp_for_each< boost::describe::describe_enumerators >([&](auto D) { - if( e == D.value ) os << D.name; - }); - - return os; -} +using boost::describe::operators::operator<<; // Generic program_options custom validator for (described) enums template < @@ -57,16 +49,8 @@ void validate(boost::any& v, const std::string& name = validators::get_single_string(values); // Convert string to enum - bool found = false; E r = {}; - - boost::mp11::mp_for_each([&](auto D){ - if( !found && std::strcmp( D.name, name.c_str() ) == 0 ) - { - found = true; - r = D.value; - } - }); + bool found = boost::describe::enum_from_string(name.c_str(), r); if (found) v = boost::any(r); diff --git a/include/boost/describe/operators.hpp b/include/boost/describe/operators.hpp index 2dd3c4a..f666e8d 100644 --- a/include/boost/describe/operators.hpp +++ b/include/boost/describe/operators.hpp @@ -160,6 +160,16 @@ template std::enable_if_t< return os; } +template ::value>> +std::ostream& operator<<(std::ostream& os, E e) +{ + boost::mp11::mp_for_each< boost::describe::describe_enumerators >([&](auto D) { + if( e == D.value ) os << D.name; + }); + + return os; +} + } // namespace operators } // namespace describe From 593506485e4e3abc33802a540d872fffda2b2a09 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Mon, 7 Mar 2022 17:33:43 +0100 Subject: [PATCH 3/3] fixup! Add program_options example with a generic custom validator for enums --- include/boost/describe/operators.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/boost/describe/operators.hpp b/include/boost/describe/operators.hpp index f666e8d..a09611c 100644 --- a/include/boost/describe/operators.hpp +++ b/include/boost/describe/operators.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -160,8 +161,10 @@ template std::enable_if_t< return os; } -template ::value>> -std::ostream& operator<<(std::ostream& os, E e) +template std::enable_if_t< + has_describe_enumerators::value, + std::basic_ostream&> + operator<<( std::basic_ostream& os, E e) { boost::mp11::mp_for_each< boost::describe::describe_enumerators >([&](auto D) { if( e == D.value ) os << D.name;