@@ -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; | |||
}; | |||
} |
@@ -46,6 +46,18 @@ namespace cppargs | |||
{ groups.emplace_back(std::forward<X>(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<typename... T_args> | |||
group::group( | |||
const group_meta& p_meta, | |||
@@ -55,4 +67,10 @@ namespace cppargs | |||
(void) (int[]) { 0, (group_init<T_args> { _options, _groups } (std::forward<T_args>(p_args)), 0)... }; | |||
} | |||
const group::option_vector_type& group::options() const | |||
{ return _options; } | |||
const group::group_vector_type& group::groups() const | |||
{ return _groups; } | |||
} |
@@ -5,6 +5,42 @@ | |||
namespace cppargs | |||
{ | |||
template<typename T_instance, typename T_arg> | |||
struct member_verify_and_forward | |||
{ | |||
template<typename T, typename T_enable = void> | |||
struct check | |||
{ | |||
template<typename X> | |||
constexpr X&& operator()(X&& x) const | |||
{ static_assert(sizeof(X) == -1, "invalid group or option type"); } | |||
}; | |||
template<typename T> | |||
struct check< | |||
std::unique_ptr<T>, | |||
utl::mp::enable_if<utl::mp::is_base_of<member_owned_group<T_instance>, T>>> | |||
{ | |||
template<typename X> | |||
constexpr X&& operator()(X&& x) const | |||
{ return std::forward<X>(x); } | |||
}; | |||
template<typename T> | |||
struct check< | |||
std::unique_ptr<T>, | |||
utl::mp::enable_if<utl::mp::is_base_of<member_option<T_instance>, T>>> | |||
{ | |||
template<typename X> | |||
constexpr X&& operator()(X&& x) const | |||
{ return std::forward<X>(x); } | |||
}; | |||
template<typename X> | |||
constexpr decltype(auto) operator()(X&& x) const | |||
{ return check<T_arg> { } (std::forward<X>(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<T_args>(p_args)...) | |||
: group (p_meta, member_verify_and_forward<T_my_instance, T_args> { } (std::forward<T_args>(p_args))...) | |||
, _predicate(p_pred) | |||
{ | |||
for (auto& o : _options) | |||
@@ -8,10 +8,6 @@ namespace cppargs | |||
struct simple_group | |||
: public group | |||
{ | |||
private: | |||
template<typename T_arg> | |||
using simple_verify_and_forward = verify_and_forward<simple_group, simple_option, T_arg>; | |||
public: | |||
/** | |||
* @brief Constructor. | |||
@@ -5,6 +5,42 @@ | |||
namespace cppargs | |||
{ | |||
template<typename T_arg> | |||
struct simple_verify_and_forward | |||
{ | |||
template<typename T, typename T_enable = void> | |||
struct check | |||
{ | |||
template<typename X> | |||
constexpr X&& operator()(X&& x) const | |||
{ static_assert(sizeof(X) == -1, "invalid group or option type"); } | |||
}; | |||
template<typename T> | |||
struct check< | |||
std::unique_ptr<T>, | |||
utl::mp::enable_if<utl::mp::is_base_of<simple_group, T>>> | |||
{ | |||
template<typename X> | |||
constexpr X&& operator()(X&& x) const | |||
{ return std::forward<X>(x); } | |||
}; | |||
template<typename T> | |||
struct check< | |||
std::unique_ptr<T>, | |||
utl::mp::enable_if<utl::mp::is_base_of<simple_option, T>>> | |||
{ | |||
template<typename X> | |||
constexpr X&& operator()(X&& x) const | |||
{ return std::forward<X>(x); } | |||
}; | |||
template<typename X> | |||
constexpr decltype(auto) operator()(X&& x) const | |||
{ return check<T_arg> { } (std::forward<X>(x)); } | |||
}; | |||
template<typename... T_args> | |||
simple_group::simple_group( | |||
const group_meta& p_meta, | |||
@@ -1,44 +1,15 @@ | |||
#pragma once | |||
#include <string> | |||
#include <cpputils/mp.h> | |||
namespace cppargs | |||
{ | |||
template<typename T_group, typename T_option, typename T_arg> | |||
struct verify_and_forward | |||
template<typename T_value, typename T_enable = void> | |||
struct argument_props | |||
{ | |||
template<typename T, typename T_enable = void> | |||
struct check | |||
{ | |||
template<typename X> | |||
constexpr X&& operator()(X&& x) const | |||
{ static_assert(sizeof(X) == -1, "invalid group or option type"); } | |||
}; | |||
template<typename T> | |||
struct check< | |||
T, | |||
utl::mp::enable_if<utl::mp::is_base_of<T_group, T>>> | |||
{ | |||
template<typename X> | |||
constexpr X&& operator()(X&& x) const | |||
{ return std::forward<X>(x); } | |||
}; | |||
template<typename T> | |||
struct check< | |||
std::unique_ptr<T>, | |||
utl::mp::enable_if<utl::mp::is_base_of<T_option, T>>> | |||
{ | |||
template<typename X> | |||
constexpr X&& operator()(X&& x) const | |||
{ return std::forward<X>(x); } | |||
}; | |||
template<typename X> | |||
constexpr decltype(auto) operator()(X&& x) const | |||
{ return check<T_arg> { } (std::forward<X>(x)); } | |||
static std::string type(bool optional = false) = delete; | |||
}; | |||
} |
@@ -0,0 +1,57 @@ | |||
#pragma once | |||
#include <list> | |||
#include <vector> | |||
#include <cpputils/misc/type_helper.h> | |||
#include <cppargs/misc/misc.h> | |||
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<std::string, void> | |||
{ | |||
static std::string type(bool optional = false) | |||
{ return enwrap_type("string", false); } | |||
}; | |||
template<typename T_value> | |||
struct argument_props<T_value, utl::mp::enable_if_c<!std::is_class<T_value>::value>> | |||
{ | |||
static std::string type(bool optional = false) | |||
{ return enwrap_type(utl::type_helper<T_value>::name(), false); } | |||
}; | |||
template<typename T_value> | |||
struct argument_props<std::list<T_value>, void> | |||
{ | |||
static std::string type(bool optional = false) | |||
{ | |||
return | |||
argument_props<T_value>::type(false) + ' ' + | |||
argument_props<T_value>::type(true) + ' ' + | |||
enwrap_type("...", true); | |||
} | |||
}; | |||
template<typename T_value> | |||
struct argument_props<std::vector<T_value>, void> | |||
{ | |||
static std::string type(bool optional = false) | |||
{ | |||
return | |||
argument_props<T_value>::type(false) + ' ' + | |||
argument_props<T_value>::type(true) + ' ' + | |||
enwrap_type("...", true); | |||
} | |||
}; | |||
} |
@@ -0,0 +1,38 @@ | |||
#pragma once | |||
#include <cppargs/group/group.h> | |||
#include <cppargs/options/option.h> | |||
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; | |||
}; | |||
} |
@@ -0,0 +1,75 @@ | |||
#pragma once | |||
#include <cpputils/misc/indent.h> | |||
#include <cppargs/misc/printing.h> | |||
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; | |||
} | |||
} |
@@ -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<typename T_value> | |||
static option_meta prepare_arguments(const option_meta& meta); | |||
}; | |||
/** | |||
@@ -1,10 +1,57 @@ | |||
#pragma once | |||
#include <cppargs/misc/misc.inl> | |||
#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<typename T_value> | |||
option_meta option_meta::prepare_arguments(const option_meta& meta) | |||
{ | |||
option_meta m = meta; | |||
if (m.arguments.empty()) | |||
m.arguments = argument_props<T_value>::type(); | |||
return m; | |||
} | |||
option::option(const option_meta& p_meta) | |||
: meta(p_meta) | |||
{ } | |||
@@ -24,6 +24,17 @@ namespace cppargs | |||
template<typename T_instance, typename T_object, typename T_value> | |||
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<typename T_instance, typename... T_args> | |||
static constexpr decltype(auto) make_group(const group_meta& meta, T_args&&... args); | |||
/** | |||
* @brief Create new group. | |||
* | |||
@@ -1,8 +1,11 @@ | |||
#pragma once | |||
#include <cpputils/misc/type_helper.h> | |||
#include <cppargs/parser/parser.h> | |||
#include <cppargs/options/member.h> | |||
#include <cppargs/misc/option_parser.h> | |||
#include <cppargs/group/member_group.inl> | |||
namespace cppargs | |||
@@ -24,7 +27,23 @@ namespace cppargs | |||
using predicate_type = decltype(predicate); | |||
using member_predicate_option_type = member_predicate_option<instance_type, value_type, predicate_type>; | |||
return std::make_unique<member_predicate_option_type>(meta, std::move(predicate)); | |||
return std::make_unique<member_predicate_option_type>( | |||
option_meta::prepare_arguments<T_value>(meta), | |||
std::move(predicate)); | |||
} | |||
template<typename T_instance, typename... T_args> | |||
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<owner_instance_type, my_instance_type, predicate_type>; | |||
return std::make_unique<group_type>(meta, std::move(predicate), std::forward<T_args>(args)...); | |||
} | |||
template<typename T_instance, typename T_object, typename T_value, typename... T_args> | |||
@@ -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; | |||
}; | |||
} |
@@ -2,6 +2,7 @@ | |||
#include <cppargs/parser/parser.h> | |||
#include <cppargs/group/group.inl> | |||
#include <cppargs/misc/printing.inl> | |||
namespace cppargs | |||
{ | |||
@@ -92,4 +93,7 @@ namespace cppargs | |||
} | |||
} | |||
void parser::print_help(std::ostream& os) const | |||
{ printing { } (os, _group); } | |||
} |
@@ -45,7 +45,9 @@ namespace cppargs | |||
using predicate_type = decltype(predicate); | |||
using simple_predicate_option_type = simple_predicate_option<predicate_type>; | |||
return std::make_unique<simple_predicate_option_type>(meta, std::move(predicate)); | |||
return std::make_unique<simple_predicate_option_type>( | |||
option_meta::prepare_arguments<T_value>(meta), | |||
std::move(predicate)); | |||
} | |||
} |
@@ -18,9 +18,9 @@ TEST(member_parser, simple_object) | |||
simple s; | |||
parser p( | |||
nullptr, | |||
parser::make_member_option<simple>({ "int", 'i' }, &simple::i), | |||
parser::make_member_option<simple>({ "string", 's' }, &simple::s), | |||
parser::make_member_option<simple>({ "double", 'd' }, &simple::d) | |||
parser::make_member_option<simple>({ 'i', "int" }, &simple::i), | |||
parser::make_member_option<simple>({ 's', "string" }, &simple::s), | |||
parser::make_member_option<simple>({ 'd', "double" }, &simple::d) | |||
); | |||
std::vector<const char*> args({ | |||
@@ -55,12 +55,12 @@ TEST(member_parser, grouped_object) | |||
grouped g; | |||
parser p( | |||
nullptr, | |||
parser::make_member_option<grouped>({ "int", 'i' }, &grouped::i), | |||
parser::make_member_option<grouped>({ "string", 's' }, &grouped::s), | |||
parser::make_member_option<grouped>({ "double", 'd' }, &grouped::d), | |||
parser::make_member_option<grouped>({ 'i', "int" }, &grouped::i), | |||
parser::make_member_option<grouped>({ 's', "string" }, &grouped::s), | |||
parser::make_member_option<grouped>({ 'd', "double" }, &grouped::d), | |||
parser::make_member_group<grouped> ({ }, &grouped::g1, | |||
parser::make_member_option<grouped::group1>({ "list", 'l' }, &grouped::group1::list), | |||
parser::make_member_option<grouped::group1>({ "vector", 'v' }, &grouped::group1::vec)) | |||
parser::make_member_option<grouped::group1>({ 'l', "list" }, &grouped::group1::list), | |||
parser::make_member_option<grouped::group1>({ 'v', "vector" }, &grouped::group1::vec)) | |||
); | |||
std::vector<const char*> args({ | |||
@@ -0,0 +1,93 @@ | |||
#include <vector> | |||
#include <gtest/gtest.h> | |||
#include <cppargs.h> | |||
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<config>; | |||
TEST(misc, print_help) | |||
{ | |||
parser p( | |||
nullptr, | |||
parser::make_member_option<config>( | |||
::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<std::string>({ }), | |||
/* database */ | |||
parser::make_member_group<config>( | |||
::cppargs::group_meta() | |||
.set_name("Database") | |||
.set_description("The following parameters are database related."), | |||
&config::database, | |||
parser::make_member_option<struct config::database>( | |||
::cppargs::option_meta() | |||
.set_long_name ("database:host") | |||
.set_description("Database host to connect to."), | |||
&config::database::host), | |||
parser::make_member_option<struct config::database>( | |||
::cppargs::option_meta() | |||
.set_long_name ("database:port") | |||
.set_description("Database port to connect to."), | |||
&config::database::port), | |||
parser::make_member_option<struct config::database>( | |||
::cppargs::option_meta() | |||
.set_long_name ("database:password") | |||
.set_description("Password to use for authenticatiom"), | |||
&config::database::password)), | |||
/* logging */ | |||
parser::make_member_group<config>( | |||
::cppargs::group_meta() | |||
.set_name("Logging"), | |||
&config::logging, | |||
parser::make_member_option<struct config::logging>( | |||
::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<struct config::logging>( | |||
::cppargs::option_meta() | |||
.set_long_name ("logging:level") | |||
.set_description("Log Level."), | |||
&config::logging::level), | |||
parser::make_member_option<struct config::logging>( | |||
::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(); | |||
} |
@@ -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<const char*> 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<const char*> 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<const char*> 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<const char*> args({ | |||
@@ -75,7 +74,7 @@ TEST(simple_parser, vector_extra_arg) | |||
std::vector<int> vec; | |||
parser p( | |||
parser::make_predicate_option({ }, [](auto){ }), | |||
parser::make_reference_option({ "vec", 'v' }, vec) | |||
parser::make_reference_option({ 'v', "vec" }, vec) | |||
); | |||
std::vector<const char*> args({ | |||
@@ -91,7 +90,7 @@ TEST(simple_parser, vector_same_arg) | |||
std::vector<int> vec; | |||
parser p( | |||
parser::make_predicate_option({ }, [](auto){ }), | |||
parser::make_reference_option({ "vec", 'v' }, vec) | |||
parser::make_reference_option({ 'v', "vec" }, vec) | |||
); | |||
std::vector<const char*> args({ | |||