diff --git a/include/cppargs/group/group.h b/include/cppargs/group/group.h index aecb45b..0a927ae 100644 --- a/include/cppargs/group/group.h +++ b/include/cppargs/group/group.h @@ -12,8 +12,11 @@ namespace cppargs */ struct group_meta { - const std::string name { "" }; //!< Name of the group. - const std::string description { "" }; //!< Description of the group. + std::string name { "" }; //!< Name of the group. + std::string description { "" }; //!< Description of the group. + + inline group_meta& set_name (const std::string& value); + inline group_meta& set_description(const std::string& value); }; /** @@ -63,6 +66,16 @@ namespace cppargs * @brief Destructor. */ virtual ~group() = default; + + /** + * @brief Get all options assigned to this group. + */ + inline const option_vector_type& options() const; + + /** + * @brief Get all subgroups assigned to this group. + */ + inline const group_vector_type& groups() const; }; } diff --git a/include/cppargs/group/group.inl b/include/cppargs/group/group.inl index bb145aa..e490672 100644 --- a/include/cppargs/group/group.inl +++ b/include/cppargs/group/group.inl @@ -46,6 +46,18 @@ namespace cppargs { groups.emplace_back(std::forward(x)); } }; + group_meta& group_meta::set_name(const std::string& value) + { + name = value; + return *this; + } + + group_meta& group_meta::set_description(const std::string& value) + { + description = value; + return *this; + } + template group::group( const group_meta& p_meta, @@ -55,4 +67,10 @@ namespace cppargs (void) (int[]) { 0, (group_init { _options, _groups } (std::forward(p_args)), 0)... }; } + const group::option_vector_type& group::options() const + { return _options; } + + const group::group_vector_type& group::groups() const + { return _groups; } + } diff --git a/include/cppargs/group/member_group.inl b/include/cppargs/group/member_group.inl index 5f77b3a..c04f9a4 100644 --- a/include/cppargs/group/member_group.inl +++ b/include/cppargs/group/member_group.inl @@ -5,6 +5,42 @@ namespace cppargs { + template + struct member_verify_and_forward + { + template + struct check + { + template + constexpr X&& operator()(X&& x) const + { static_assert(sizeof(X) == -1, "invalid group or option type"); } + }; + + template + struct check< + std::unique_ptr, + utl::mp::enable_if, T>>> + { + template + constexpr X&& operator()(X&& x) const + { return std::forward(x); } + }; + + template + struct check< + std::unique_ptr, + utl::mp::enable_if, T>>> + { + template + constexpr X&& operator()(X&& x) const + { return std::forward(x); } + }; + + template + constexpr decltype(auto) operator()(X&& x) const + { return check { } (std::forward(x)); } + }; + template< typename T_owner_instance, typename T_my_instance, @@ -16,7 +52,7 @@ namespace cppargs const group_meta& p_meta, resolve_instance_pred_type&& p_pred, T_args&&... p_args) - : group (p_meta, std::forward(p_args)...) + : group (p_meta, member_verify_and_forward { } (std::forward(p_args))...) , _predicate(p_pred) { for (auto& o : _options) diff --git a/include/cppargs/group/simple_group.h b/include/cppargs/group/simple_group.h index 88c6879..d99bd33 100644 --- a/include/cppargs/group/simple_group.h +++ b/include/cppargs/group/simple_group.h @@ -8,10 +8,6 @@ namespace cppargs struct simple_group : public group { - private: - template - using simple_verify_and_forward = verify_and_forward; - public: /** * @brief Constructor. diff --git a/include/cppargs/group/simple_group.inl b/include/cppargs/group/simple_group.inl index 82dbc44..1eb8592 100644 --- a/include/cppargs/group/simple_group.inl +++ b/include/cppargs/group/simple_group.inl @@ -5,6 +5,42 @@ namespace cppargs { + template + struct simple_verify_and_forward + { + template + struct check + { + template + constexpr X&& operator()(X&& x) const + { static_assert(sizeof(X) == -1, "invalid group or option type"); } + }; + + template + struct check< + std::unique_ptr, + utl::mp::enable_if>> + { + template + constexpr X&& operator()(X&& x) const + { return std::forward(x); } + }; + + template + struct check< + std::unique_ptr, + utl::mp::enable_if>> + { + template + constexpr X&& operator()(X&& x) const + { return std::forward(x); } + }; + + template + constexpr decltype(auto) operator()(X&& x) const + { return check { } (std::forward(x)); } + }; + template simple_group::simple_group( const group_meta& p_meta, diff --git a/include/cppargs/misc/misc.h b/include/cppargs/misc/misc.h index 9128ddb..4085a1b 100644 --- a/include/cppargs/misc/misc.h +++ b/include/cppargs/misc/misc.h @@ -1,44 +1,15 @@ #pragma once +#include #include namespace cppargs { - template - struct verify_and_forward + template + struct argument_props { - template - struct check - { - template - constexpr X&& operator()(X&& x) const - { static_assert(sizeof(X) == -1, "invalid group or option type"); } - }; - - template - struct check< - T, - utl::mp::enable_if>> - { - template - constexpr X&& operator()(X&& x) const - { return std::forward(x); } - }; - - template - struct check< - std::unique_ptr, - utl::mp::enable_if>> - { - template - constexpr X&& operator()(X&& x) const - { return std::forward(x); } - }; - - template - constexpr decltype(auto) operator()(X&& x) const - { return check { } (std::forward(x)); } + static std::string type(bool optional = false) = delete; }; } diff --git a/include/cppargs/misc/misc.inl b/include/cppargs/misc/misc.inl new file mode 100644 index 0000000..8499dda --- /dev/null +++ b/include/cppargs/misc/misc.inl @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +#include +#include + +namespace cppargs +{ + + inline std::string enwrap_type(const std::string& type, bool optional) + { + auto opn = optional ? "(" : "<"; + auto cls = optional ? ")" : ">"; + return std::string(opn) + type + cls; + } + + template<> + struct argument_props + { + static std::string type(bool optional = false) + { return enwrap_type("string", false); } + }; + + template + struct argument_props::value>> + { + static std::string type(bool optional = false) + { return enwrap_type(utl::type_helper::name(), false); } + }; + + template + struct argument_props, void> + { + static std::string type(bool optional = false) + { + return + argument_props::type(false) + ' ' + + argument_props::type(true) + ' ' + + enwrap_type("...", true); + } + }; + + template + struct argument_props, void> + { + static std::string type(bool optional = false) + { + return + argument_props::type(false) + ' ' + + argument_props::type(true) + ' ' + + enwrap_type("...", true); + } + }; + +} diff --git a/include/cppargs/misc/printing.h b/include/cppargs/misc/printing.h new file mode 100644 index 0000000..345f639 --- /dev/null +++ b/include/cppargs/misc/printing.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +namespace cppargs +{ + + struct printing + { + public: + /** + * @brief Print the passed group to the passed stream. + * + * @param[in] os Stream to print group to. + * @param[in] g Group to print to stream. + */ + inline void operator()(std::ostream& os, const group& g) const; + + private: + /** + * @brief Print the passed group to the passed stream. + * + * @param[in] os Stream to print group to. + * @param[in] g Group to print to stream. + */ + inline void print_group(std::ostream& os, const group& g) const; + + /** + * @brief Print the passed option to the passed stream. + * + * @param[in] os Stream to print group to. + * @param[in] o Option to print to stream. + */ + inline void print_option(std::ostream& os, const option& o) const; + }; + +} diff --git a/include/cppargs/misc/printing.inl b/include/cppargs/misc/printing.inl new file mode 100644 index 0000000..4b03a44 --- /dev/null +++ b/include/cppargs/misc/printing.inl @@ -0,0 +1,75 @@ +#pragma once + +#include +#include + +namespace cppargs +{ + + void printing::operator()(std::ostream& os, const group& g) const + { print_group(os, g); } + + void printing::print_group(std::ostream& os, const group& g) const + { + using namespace ::utl; + + /* meta */ + if (!g.meta.name.empty()) + { + os << indent << "-= " << g.meta.name << " =-" + << std::endl; + } + if (!g.meta.description.empty()) + { + os << indent << g.meta.description + << std::endl; + } + + os << incindent; + + /* options */ + bool has_option = false; + for (auto& o : g.options()) + { + if (!has_option) + has_option = true; + else + os << std::endl; + print_option(os, *o); + } + if (has_option) + os << std::endl; + + os << decindent; + + /* groups */ + for (auto& i : g.groups()) + { + print_group(os, *i); + } + } + + void printing::print_option(std::ostream& os, const option& o) const + { + using namespace ::utl; + auto& m = o.meta; + os << indent; + if (m.short_name != '\0') + os << '-' << m.short_name; + if (m.short_name != '\0' && !m.long_name.empty()) + os << '|'; + if (!m.long_name.empty()) + os << "--" << m.long_name; + if (!m.arguments.empty()) + os << " " << m.arguments; + os << incindent; + if (!m.description.empty()) + os << indent << m.description; + if (!m.default_value.empty()) + os << indent << "Default value: " << m.default_value; + if (m.mandatory) + os << indent << "This option is mandatory."; + os << decindent; + } + +} diff --git a/include/cppargs/options/option.h b/include/cppargs/options/option.h index 23bf80a..5efbad3 100644 --- a/include/cppargs/options/option.h +++ b/include/cppargs/options/option.h @@ -11,11 +11,22 @@ namespace cppargs */ struct option_meta { - const std::string long_name { "" }; //!< long name of the option (without the prepending --) - const char short_name { '\0' }; //!< short option of the option (without the prependding -) - const std::string description { "" }; //!< description for this option - const std::string default_value { "" }; //!< default value for this option - const bool optional { true }; //!< TRUE if this option is optional, FALSE otherwise + char short_name { '\0' }; //!< short option of the option (without the prependding -) + std::string long_name { "" }; //!< long name of the option (without the prepending --) + std::string description { "" }; //!< description for this option + std::string arguments { "" }; //!< arguments to pass to this option + std::string default_value { "" }; //!< default value for this option + bool mandatory { false }; //!< TRUE if this option is optional, FALSE otherwise + + inline option_meta& set_short_name (char value); + inline option_meta& set_long_name (const std::string& value); + inline option_meta& set_description (const std::string& value); + inline option_meta& set_arguments (const std::string& value); + inline option_meta& set_default_value (const std::string& value); + inline option_meta& set_mandatory (bool value = true); + + template + static option_meta prepare_arguments(const option_meta& meta); }; /** diff --git a/include/cppargs/options/option.inl b/include/cppargs/options/option.inl index aa6fb54..c7234d1 100644 --- a/include/cppargs/options/option.inl +++ b/include/cppargs/options/option.inl @@ -1,10 +1,57 @@ #pragma once +#include + #include "option.h" namespace cppargs { + option_meta& option_meta::set_short_name(char value) + { + short_name = value; + return *this; + } + + option_meta& option_meta::set_long_name(const std::string& value) + { + long_name = value; + return *this; + } + + option_meta& option_meta::set_description(const std::string& value) + { + description = value; + return *this; + } + + option_meta& option_meta::set_arguments(const std::string& value) + { + arguments = value; + return *this; + } + + option_meta& option_meta::set_default_value(const std::string& value) + { + default_value = value; + return *this; + } + + option_meta& option_meta::set_mandatory(bool value) + { + mandatory = value; + return *this; + } + + template + option_meta option_meta::prepare_arguments(const option_meta& meta) + { + option_meta m = meta; + if (m.arguments.empty()) + m.arguments = argument_props::type(); + return m; + } + option::option(const option_meta& p_meta) : meta(p_meta) { } diff --git a/include/cppargs/parser/member_parser.h b/include/cppargs/parser/member_parser.h index 44af1f0..d922575 100644 --- a/include/cppargs/parser/member_parser.h +++ b/include/cppargs/parser/member_parser.h @@ -24,6 +24,17 @@ namespace cppargs template static constexpr decltype(auto) make_member_option(const option_meta& meta, T_value T_object::*member); + /** + * @brief Create new group. + * + * @param[in] meta Meta data of the new option. + * @param[in] args Subgroups and options. + * + * @return Created group. + */ + template + static constexpr decltype(auto) make_group(const group_meta& meta, T_args&&... args); + /** * @brief Create new group. * diff --git a/include/cppargs/parser/member_parser.inl b/include/cppargs/parser/member_parser.inl index a1849a1..c54347f 100644 --- a/include/cppargs/parser/member_parser.inl +++ b/include/cppargs/parser/member_parser.inl @@ -1,8 +1,11 @@ #pragma once +#include + #include #include #include + #include namespace cppargs @@ -24,7 +27,23 @@ namespace cppargs using predicate_type = decltype(predicate); using member_predicate_option_type = member_predicate_option; - return std::make_unique(meta, std::move(predicate)); + return std::make_unique( + option_meta::prepare_arguments(meta), + std::move(predicate)); + } + + template + constexpr decltype(auto) member_parser_base::make_group(const group_meta& meta, T_args&&... args) + { + using owner_instance_type = T_instance; + using my_instance_type = T_instance; + + auto predicate = [](owner_instance_type* instance) + { return instance; }; + + using predicate_type = decltype(predicate); + using group_type = member_group; + return std::make_unique(meta, std::move(predicate), std::forward(args)...); } template diff --git a/include/cppargs/parser/parser.h b/include/cppargs/parser/parser.h index 8dce997..9047e47 100644 --- a/include/cppargs/parser/parser.h +++ b/include/cppargs/parser/parser.h @@ -64,7 +64,7 @@ namespace cppargs * * @param[in] os Stream to write help to. */ - void print_help(std::ostream& os) const; + inline void print_help(std::ostream& os) const; }; } diff --git a/include/cppargs/parser/parser.inl b/include/cppargs/parser/parser.inl index 49ac80b..e9142a5 100644 --- a/include/cppargs/parser/parser.inl +++ b/include/cppargs/parser/parser.inl @@ -2,6 +2,7 @@ #include #include +#include namespace cppargs { @@ -92,4 +93,7 @@ namespace cppargs } } + void parser::print_help(std::ostream& os) const + { printing { } (os, _group); } + } diff --git a/include/cppargs/parser/simple_parser.inl b/include/cppargs/parser/simple_parser.inl index 1dcdc15..ca2bb07 100644 --- a/include/cppargs/parser/simple_parser.inl +++ b/include/cppargs/parser/simple_parser.inl @@ -45,7 +45,9 @@ namespace cppargs using predicate_type = decltype(predicate); using simple_predicate_option_type = simple_predicate_option; - return std::make_unique(meta, std::move(predicate)); + return std::make_unique( + option_meta::prepare_arguments(meta), + std::move(predicate)); } } diff --git a/test/cppargs/member_parser.cpp b/test/cppargs/member_parser.cpp index b18adc2..999570e 100644 --- a/test/cppargs/member_parser.cpp +++ b/test/cppargs/member_parser.cpp @@ -18,9 +18,9 @@ TEST(member_parser, simple_object) simple s; parser p( nullptr, - parser::make_member_option({ "int", 'i' }, &simple::i), - parser::make_member_option({ "string", 's' }, &simple::s), - parser::make_member_option({ "double", 'd' }, &simple::d) + parser::make_member_option({ 'i', "int" }, &simple::i), + parser::make_member_option({ 's', "string" }, &simple::s), + parser::make_member_option({ 'd', "double" }, &simple::d) ); std::vector args({ @@ -55,12 +55,12 @@ TEST(member_parser, grouped_object) grouped g; parser p( nullptr, - parser::make_member_option({ "int", 'i' }, &grouped::i), - parser::make_member_option({ "string", 's' }, &grouped::s), - parser::make_member_option({ "double", 'd' }, &grouped::d), + parser::make_member_option({ 'i', "int" }, &grouped::i), + parser::make_member_option({ 's', "string" }, &grouped::s), + parser::make_member_option({ 'd', "double" }, &grouped::d), parser::make_member_group ({ }, &grouped::g1, - parser::make_member_option({ "list", 'l' }, &grouped::group1::list), - parser::make_member_option({ "vector", 'v' }, &grouped::group1::vec)) + parser::make_member_option({ 'l', "list" }, &grouped::group1::list), + parser::make_member_option({ 'v', "vector" }, &grouped::group1::vec)) ); std::vector args({ diff --git a/test/cppargs/misc.cpp b/test/cppargs/misc.cpp new file mode 100644 index 0000000..fe338af --- /dev/null +++ b/test/cppargs/misc.cpp @@ -0,0 +1,93 @@ +#include +#include + +#include + +struct config +{ + std::string config; + + struct database + { + std::string host; + uint16_t port; + std::string password; + } + database; + + struct logging + { + std::string file; + std::string level; + std::string name; + } + logging; +}; + +using parser = ::cppargs::member_parser; + +TEST(misc, print_help) +{ + parser p( + nullptr, + + parser::make_member_option( + ::cppargs::option_meta() + .set_short_name ('c') + .set_long_name ("config") + .set_arguments (::cppargs::enwrap_type("filename", false)) + .set_description("Specify a configuration file to load config values from.") + .set_mandatory (true), + &config::config), + + // parser::make_group({ }), + + /* database */ + parser::make_member_group( + ::cppargs::group_meta() + .set_name("Database") + .set_description("The following parameters are database related."), + &config::database, + parser::make_member_option( + ::cppargs::option_meta() + .set_long_name ("database:host") + .set_description("Database host to connect to."), + &config::database::host), + parser::make_member_option( + ::cppargs::option_meta() + .set_long_name ("database:port") + .set_description("Database port to connect to."), + &config::database::port), + parser::make_member_option( + ::cppargs::option_meta() + .set_long_name ("database:password") + .set_description("Password to use for authenticatiom"), + &config::database::password)), + + /* logging */ + parser::make_member_group( + ::cppargs::group_meta() + .set_name("Logging"), + &config::logging, + parser::make_member_option( + ::cppargs::option_meta() + .set_long_name ("logging:file") + .set_arguments (::cppargs::enwrap_type("filename", false)) + .set_description("File to write log output to."), + &config::logging::file), + parser::make_member_option( + ::cppargs::option_meta() + .set_long_name ("logging:level") + .set_description("Log Level."), + &config::logging::level), + parser::make_member_option( + ::cppargs::option_meta() + .set_long_name ("logging:name") + .set_description("Default name of the logger."), + &config::logging::name))); + + std::ostringstream ss; + p.print_help(ss); + + std::cout << ss.str(); +} diff --git a/test/cppargs/simple_parser.cpp b/test/cppargs/simple_parser.cpp index dc5aec0..1da4374 100644 --- a/test/cppargs/simple_parser.cpp +++ b/test/cppargs/simple_parser.cpp @@ -7,11 +7,10 @@ using parser = ::cppargs::simple_parser; TEST(simple_parser, short_name_seperate_arg) { - std::string s; parser p( nullptr, - parser::make_reference_option({ "string", 's' }, s) + parser::make_reference_option({ 's', "string" }, s) ); std::vector args({ @@ -27,7 +26,7 @@ TEST(simple_parser, long_name_seperate_arg) int i; parser p( nullptr, - parser::make_reference_option({ "int", 'i' }, i) + parser::make_reference_option({ 'i', "int" }, i) ); std::vector args({ @@ -43,7 +42,7 @@ TEST(simple_parser, short_name_same_arg) double d; parser p( nullptr, - parser::make_reference_option({ "double", 'd' }, d) + parser::make_reference_option({ 'd', "double" }, d) ); std::vector args({ @@ -59,7 +58,7 @@ TEST(simple_parser, long_name_same_arg) std::string s; parser p( nullptr, - parser::make_reference_option({ "string", 's' }, s) + parser::make_reference_option({ 's', "string" }, s) ); std::vector args({ @@ -75,7 +74,7 @@ TEST(simple_parser, vector_extra_arg) std::vector vec; parser p( parser::make_predicate_option({ }, [](auto){ }), - parser::make_reference_option({ "vec", 'v' }, vec) + parser::make_reference_option({ 'v', "vec" }, vec) ); std::vector args({ @@ -91,7 +90,7 @@ TEST(simple_parser, vector_same_arg) std::vector vec; parser p( parser::make_predicate_option({ }, [](auto){ }), - parser::make_reference_option({ "vec", 'v' }, vec) + parser::make_reference_option({ 'v', "vec" }, vec) ); std::vector args({