8#include <kernel/runtime.hpp>
10#include <kernel/util/property_map.hpp>
11#include <kernel/util/string.hpp>
67 template<
typename T,
typename A>
77 template<
typename T,
typename =
void>
84 struct has_input_operator<T, std::void_t<decltype((std::declval<std::istringstream&>() >> std::declval<T&>()))>>
94 template<
typename T,
typename A>
99 static void parse_collection(std::any& any,
const String& s)
101 std::deque<T_> collection;
105 if(!value_str.parse(tmp))
107 throw std::invalid_argument(
"Parsing failed for string '" + s +
"'");
109 collection.push_back(tmp);
116 template<
typename T_>
117 static void parse_single(std::any& any,
const String& s)
119 if(!s.parse(std::any_cast<T_&>(any)))
121 throw std::invalid_argument(
"Parsing failed for string '" + s +
"'");
140 template<
typename T_>
141 static void (*make_parser())(std::any&,
const String&)
149 if constexpr(!can_default_parse_v<T_>)
155 if constexpr(is_std_deque_v<T_>)
157 return parse_collection<typename T_::value_type>;
161 return parse_single<T_>;
176 template<
typename T_>
177 static void (*make_printer())(std::ostream&,
const std::any&)
179 if constexpr(is_std_deque_v<T_>)
181 return [](std::ostream& s,
const std::any& v)
184 for(
const auto& i : std::any_cast<const T_&>(v))
193 return [](std::ostream& s,
const std::any& v) { s << std::any_cast<const T_&>(v); };
204 enum class ParameterType : std::uint8_t
234 using PrinterType = std::function<void(std::ostream&,
const std::any&)>;
237 using Need = std::pair<std::weak_ptr<ParameterCore>, std::function<bool(
const std::any&)>>;
240 ParameterType type = ParameterType::flag;
243 String environment_variable{
""};
244 String property_path{
""};
253 bool required =
false;
254 bool set_by_user =
false;
255 std::deque<Need> needs;
258 std::deque<String> arguments;
259 std::any default_value;
268 template<
typename T_>
270 default_value(def_value),
271 value(default_value),
272 printer(Intern::make_printer<T_>())
274 if constexpr(std::is_same_v<T_, bool>)
277 type = ParameterType::flag;
279 else if constexpr(Intern::is_std_deque_v<T_>)
282 type = ParameterType::collection_option;
286 type = ParameterType::option;
306 void validate(std::deque<String>& errors)
const;
313 void parse(std::deque<String>& errors);
321 template<
typename T_>
322 class ParameterBuilder;
329 template<
typename T_>
333 std::shared_ptr<Intern::ParameterCore> _core;
336 template<
typename U_>
339 explicit Parameter(std::shared_ptr<Intern::ParameterCore>&& core) : _core(std::move(core))
343 explicit Parameter(std::shared_ptr<Intern::ParameterCore> core) : _core(std::move(core))
354 return _core->set_by_user;
360 return std::any_cast<const T_&>(_core->value);
366 return std::any_cast<const T_&>(_core->value);
372 return std::any_cast<const T_>(&(_core->value));
377 template<
typename T_>
381 std::shared_ptr<Intern::ParameterCore> _core;
384 explicit ParameterBuilder(std::shared_ptr<Intern::ParameterCore> core) : _core(std::move(core))
395 XASSERTM(s.size() == 2,
"Short flag must have length two");
396 XASSERTM(s[0] ==
'-',
"Short must start with a '-'");
397 XASSERTM(s[1] !=
'-',
"Short must not end with a '-'");
399 _core->short_flag = std::move(s);
425 _core->environment_variable = std::move(s);
440 _core->property_path = std::move(s);
505 _core->custom_name = std::move(s);
527 const auto lambda = [parser_fn](std::any& any,
const String& s) { any = parser_fn(s); };
528 _core->parser = lambda;
551 XASSERTM(validator_fn !=
nullptr,
"Validator must be valid function pointer!");
562 const auto lambda = [validator_fn](
const std::any& any) { validator_fn(std::any_cast<const T_&>(any)); };
563 _core->validator = lambda;
588 XASSERTM(validator_fn !=
nullptr,
"Validator must be valid function pointer!");
599 const auto lambda = [validator_fn](
const std::any& any)
601 if(!validator_fn(std::any_cast<const T_&>(any)))
603 throw std::invalid_argument(
"Failed boolean test");
606 _core->validator = lambda;
643 template<
typename U_>
646 XASSERTM(parameter._core !=
nullptr,
"Needed parameter must be defined already!");
648 _core->needs.emplace_back(parameter._core, [](
const std::any& ) { return true; });
674 template<
typename U_>
677 XASSERTM(parameter._core !=
nullptr,
"Needed parameter must be defined already!");
679 _core->needs.emplace_back(
681 [condition](
const std::any&
value) { return condition(std::any_cast<const T_&>(value)); });
692 if(_core->placeholder.empty())
694 if(!_core->long_flag.empty())
696 _core->placeholder = _core->long_flag.substr(2);
698 else if(!_core->short_flag.empty())
700 _core->placeholder = _core->short_flag.substr(1);
706 _core->parser = Intern::make_parser<T_>();
836 std::deque<std::shared_ptr<Intern::ParameterCore>> _parameters;
837 std::deque<String> _errors;
842 static constexpr int priority_default = 0;
843 static constexpr int priority_env = 1;
844 static constexpr int priority_property = 2;
845 static constexpr int priority_cli = 3;
848 const std::deque<String>& errors()
const
862 _description = std::move(description);
878 [](
auto& a,
auto& b) { return a->name().compare_no_case(b->name()) <= 0; });
880 std::stringstream stream;
882 if(!_description.empty())
884 stream << _description <<
"\n\n";
886 stream <<
"Usage: " << _program <<
" [OPTIONS]\n\n";
887 stream <<
"Options:\n";
889 for(
const auto& core : _parameters)
911 [](
auto& a,
auto& b) { return a->name().compare_no_case(b->name()) <= 0; });
913 std::stringstream stream;
923 for(
const auto& core : _parameters)
925 XASSERT(core->value.has_value());
927 stream << core->name().pad_back(40,
'.');
930 switch(core->priority)
932 case priority_default: stream <<
"default |";
break;
933 case priority_env: stream <<
"env |";
break;
934 case priority_property: stream <<
"property |";
break;
935 case priority_cli: stream <<
"cli |";
break;
936 default: stream <<
" |";
939 if(core->type == Intern::ParameterType::flag)
941 stream << (std::any_cast<const bool&>(core->value) ?
"true" :
"false");
946 core->printer(stream, core->value);
966 [[nodiscard]]
bool parse(
int argc,
const char*
const* argv,
const PropertyMap* property_map =
nullptr)
977 for(
auto& core : _parameters)
986 if(property_map !=
nullptr)
988 _collect_property_map(property_map);
992 _collect_args(argc, argv);
995 for(
auto& core : _parameters)
997 core->parse(_errors);
1002 return _errors.empty();
1014 return parse(0,
nullptr, &property_map);
1028 [[nodiscard]]
bool parse(
int argc,
const char*
const* argv,
const String& property_map_path)
1031 pmap.
read(property_map_path);
1032 return parse(argc, argv, &pmap);
1045 pmap.
read(property_map_path);
1046 return parse(0,
nullptr, &pmap);
1057 template<
typename T_>
1061 std::shared_ptr<Intern::ParameterCore> core = std::make_shared<Intern::ParameterCore>(std::forward<T_>(default_value));
1066 _parameters.push_back(core);
1077 std::set<String> short_flags;
1078 std::set<String> long_flags;
1079 std::set<String> environment_variables;
1080 std::set<String> property_paths;
1084 auto handle_flag = [&](std::set<String>& set,
const String& s)
1091 if(set.find(s) != set.end())
1093 _errors.push_back(
"Error: Flag " + s +
" is used by multiple parameters");
1102 for(
auto& core : _parameters)
1104 handle_flag(short_flags, core->short_flag);
1105 handle_flag(long_flags, core->long_flag);
1106 handle_flag(environment_variables, core->environment_variable);
1107 handle_flag(property_paths, core->property_path);
1115 for(
auto& core : _parameters)
1117 const char*
value = std::getenv(core->environment_variable.c_str());
1118 if(
value !=
nullptr)
1121 core->add_argument(s, priority_env);
1126 void _collect_property_map(
const PropertyMap* property_map)
1128 for(
auto& core : _parameters)
1130 auto [string,
success] = property_map->query(core->property_path);
1133 core->add_argument(
string, priority_property);
1138 void _collect_args(
int argc,
const char*
const* argv)
1145 _program = String(argv[0]);
1147 std::deque<String> args;
1149 for(
int i(1); i < argc; i++)
1151 args.emplace_back(argv[i]);
1155 while(idx < args.size())
1157 const String& flag = args[idx++];
1164 bool handled =
false;
1165 for(
auto& core : _parameters)
1167 if(flag.compare_no_case(core->short_flag) != 0 && flag.compare_no_case(core->long_flag) != 0)
1174 if(core->type == Intern::ParameterType::flag)
1178 core->value = !std::any_cast<const bool&>(core->default_value);
1179 core->set_by_user =
true;
1181 else if(core->type == Intern::ParameterType::option)
1184 core->add_argument(args[idx++], priority_cli);
1186 else if(core->type == Intern::ParameterType::collection_option)
1189 while(idx < args.size() && !is_flag(args[idx]))
1191 core->add_argument(args[idx++], priority_cli);
1199 _errors.push_back(
"Error: Unknown flag " + flag);
1202 while(idx < args.size() && !is_flag(args[idx]))
1213 for(
auto& core : _parameters)
1215 core->validate(_errors);
1223 core.short_flag.empty() && core.long_flag.empty() && core.property_path.empty() &&
1224 core.environment_variable.empty())
1231 auto format_flag = [&s](Intern::ParameterType type,
const String& flag,
const String& placeholder)
1233 if(type == Intern::ParameterType::flag)
1237 if(type == Intern::ParameterType::option)
1239 s << flag <<
" <" << placeholder <<
">";
1241 if(type == Intern::ParameterType::collection_option)
1243 s << flag <<
" <" << placeholder <<
"s...>";
1247 auto format_other = [&s](
const std::string_view prefix,
const String&
value)
1248 { s <<
"[" << prefix <<
":" <<
value <<
"]"; };
1252 bool has_prev =
false;
1253 if(!core.short_flag.empty())
1255 format_flag(core.type, core.short_flag, core.placeholder);
1259 if(!core.long_flag.empty())
1266 format_flag(core.type, core.long_flag, core.placeholder);
1271 if(!core.property_path.empty())
1278 format_other(
"property", core.property_path);
1283 if(!core.environment_variable.empty())
1290 format_other(
"env", core.environment_variable);
1302 s <<
"\t\tRequired argument.\n";
1307 s <<
"\t\t" << line.trim() <<
"\n";
1313 static bool is_flag(
const String& s)
1315 return !s.empty() && s[0] ==
'-';
#define XABORTM(msg)
Abortion macro definition with custom message.
#define XASSERT(expr)
Assertion macro definition.
#define XASSERTM(expr, msg)
Assertion macro definition with custom message.
Argument parser base class.
String display()
Produce formatted output of all values in this parser.
ParameterBuilder< T_ > parameter(T_ &&default_value=T_{})
Add a Parameter to this parser.
void _validate()
Validate parameters after parsing.
bool parse(const String &property_map_path)
Populate this parser.
static void parameter_help(const Intern::ParameterCore &core, std::stringstream &s)
Write flags, property path, environment variable and help text for given parameter to stringstream.
bool parse(int argc, const char *const *argv, const PropertyMap *property_map=nullptr)
Populate this parser.
void set_description(String &&description)
Set program description.
bool _check_for_unique_parameters()
bool parse(const PropertyMap &property_map)
Populate this parser.
String help_text()
Generate help text for this parser.
bool parse(int argc, const char *const *argv, const String &property_map_path)
Populate this parser.
Type-erased core of a Parameter.
String name() const
Tries to create a user-readable name for this parameter.
std::pair< std::weak_ptr< ParameterCore >, std::function< bool(const std::any &)> > Need
Type of "A needs B" declaration with condition.
void parse(std::deque< String > &errors)
Parse this parameter.
std::function< void(std::ostream &, const std::any &)> PrinterType
Type of type-erased printer function.
std::function< void(std::any &, const String &)> ParserType
Type of type-erased parser function.
std::function< void(const std::any &)> ValidatorType
Type of type-erased validator function.
ParameterCore(T_ def_value)
Constructor.
void validate(std::deque< String > &errors) const
Validate this parameter.
void reset()
Reset this parameter to default.
void add_argument(const String &s, int incoming_priority)
Add an argument to this parameter.
Builder-pattern for defining parameters. See methods for details.
ParameterBuilder & env(String &&s)
Set the environment variable for a parameter.
ParameterBuilder & validator(void(*validator_fn)(const T_ &))
Set a validator for this paramater.
ParameterBuilder & validator(bool(*validator_fn)(const T_ &))
Set a validator for this paramater.
ParameterBuilder & property(String &&s)
Set the property map path for a parameter.
ParameterBuilder & required()
Mark this parameter as required.
ParameterBuilder & needs(const Parameter< U_ > ¶meter)
Mark another parameter as needed by this one.
ParameterBuilder & short_flag(String &&s)
Set the short flag for a parameter.
ParameterBuilder & name(String &&s)
Set a name for this parameter.
ParameterBuilder & placeholder(String &&s)
Set a placeholder text for the argument of this parameter.
ParameterBuilder & needs_if(const Parameter< U_ > ¶meter, bool(*condition)(const T_ &))
Conditionally mark another parameter as needed by this one.
ParameterBuilder & parser(T_(*parser_fn)(const String &))
Set a custom parser for this parameter.
ParameterBuilder & long_flag(String &&s)
Set the long flag for a parameter.
ParameterBuilder & help_string(String &&s)
Set help string for this parameter.
Parameter of an ArgParser.
const T_ * operator->() const
Accessor for parameter value.
const T_ & operator*() const
Accessor for parameter value.
const T_ & value() const
Accessor for parameter value.
A class organizing a tree of key-value pairs.
void read(String filename, bool replace=true)
Parses a file in INI-format.
String class implementation.
std::deque< String > split_by_whitespaces() const
Splits the string by white-spaces.
String pad_back(size_type len, char c=' ') const
Pads the back of the string up to a desired length.
std::deque< String > split_by_charset(const String &charset) const
Splits the string by a delimiter charset.
@ success
solving successful (convergence criterion fulfilled)
String stringify(const T_ &item)
Converts an item into a String.
@ value
specifies whether the space should supply basis function values
std::uint64_t Index
Index data type.
Check if type is some instantiation of std::deque.