25 #ifdef MEMILIO_HAS_JSONCPP
40 #include <string_view>
41 #include <unordered_map>
54 template <
class Parameter>
55 const std::string get_name(
const Parameter& p)
61 template <
class Parameter>
62 const std::string get_alias(
const Parameter& p)
64 if constexpr (
requires { p.alias(); }) {
73 template <
class Parameter>
74 const std::string get_description(
const Parameter& p)
76 if constexpr (
requires { p.description(); }) {
77 return p.description();
85 template <
class Parameter>
86 constexpr
bool get_is_required(
const Parameter& p)
88 if constexpr (
requires { p.is_required(); }) {
89 return p.is_required();
97 struct DatalessParameter {
98 std::string name, alias =
"", description =
"";
99 bool is_required =
false;
103 struct OptionalFields {
104 std::string alias =
"", description =
"";
105 bool is_required =
false;
109 enum class IdentifierType
125 Identifier(std::string str, IdentifierType t)
126 : string(
std::move(str))
137 static IOResult<Identifier> parse(
const std::string& str)
140 return mio::success(Identifier(str, IdentifierType::Name));
142 else if (is_alias(str)) {
143 return mio::success(Identifier(str, IdentifierType::Alias));
155 static Identifier make_raw(
const std::string& raw_name)
157 return {raw_name, IdentifierType::Raw};
161 static bool is_name(std::string_view unverified_id)
164 return (unverified_id.size() > 2) && (unverified_id[0] ==
'-' && unverified_id[1] ==
'-');
168 static bool is_alias(std::string_view unverified_id)
171 return (unverified_id.size() > 1) && (unverified_id[0] ==
'-') && (unverified_id[1] !=
'-');
175 static bool is_option(std::string_view unverified_id)
177 return is_name(unverified_id) || is_alias(unverified_id);
181 inline const std::string_view strip()
const
184 case IdentifierType::Name:
185 return std::string_view{
string}.substr(2);
186 case IdentifierType::Alias:
187 return std::string_view{
string}.substr(1);
188 case IdentifierType::Raw:
189 return std::string_view{
string};
191 assert(
false &&
"Invalid IdentifierType");
193 return std::string_view{};
197 inline bool matches_parameter(
const DatalessParameter& p)
const
200 case IdentifierType::Name:
201 return p.name == strip();
202 case IdentifierType::Alias:
203 return p.alias == strip();
204 case IdentifierType::Raw:
205 return (p.name ==
string) || (
string.size() > 0 && p.alias == string);
207 assert(
false &&
"Invalid IdentifierType");
217 struct PresetOptions {
218 const inline static DatalessParameter help{
219 .name =
"help", .alias =
"h", .description =
"Show this dialogue and exit.", .is_required =
false};
221 const inline static DatalessParameter print_option{
222 .name =
"print_option",
225 "Use with parameter option name(s) without \"--\" as value(s). Prints the current values of specified "
226 "options in their correct json format, then exits.",
227 .is_required =
false};
229 const inline static DatalessParameter read_from_json{
230 .name =
"read_from_json",
233 "Takes a filepath as value. Reads and assigns parameter option values from the specified json file.",
234 .is_required =
false};
236 const inline static DatalessParameter write_to_json{
237 .name =
"write_to_json",
240 "Takes a filepath as value. Writes current values of all parameter options to the specified json file.",
241 .is_required =
false};
243 const inline static std::vector<DatalessParameter> all_presets{help, print_option, read_from_json, write_to_json};
252 class AbstractParameter :
public DatalessParameter
262 template <
class Type>
263 AbstractParameter(
mio::Tag<Type>,
const DatalessParameter& p, std::shared_ptr<void>&&
value)
264 : DatalessParameter(p)
266 , m_serialize([](const
std::shared_ptr<void>& param) ->
IOResult<Json::Value> {
267 return mio::serialize_json(*
static_cast<Type*
>(param.get()));
269 , m_set([](std::shared_ptr<void>& param,
const std::string& name,
const Json::Value& json) -> IOResult<void> {
272 auto param_result = mio::deserialize_json(json,
mio::Tag<Type>());
275 *(
static_cast<Type*
>(param.get())) = std::move(param_result.value());
280 std::string msg =
"While setting \"" + name +
"\": " + param_result.error().message();
281 if (param_result.error().message() ==
"Json value is not a string.") {
282 msg +=
" Try using escaped quotes (\\\") around your input strings.";
294 template <
class Param>
296 : AbstractParameter(
mio::
Tag<typename Param::Type>{},
297 DatalessParameter{get_name(Param{}), get_alias(Param{}), get_description(Param{}),
298 get_is_required(Param{})},
299 std::shared_ptr<void>(
static_cast<void*
>(&
value), [](
void*) {}))
307 template <
class Param>
309 : AbstractParameter(other)
318 IOResult<Json::Value>
get()
const
320 auto json_result = m_serialize(m_data);
325 std::string msg =
"While getting \"" + name() +
"\": " + json_result.error().message();
335 IOResult<void> set(
const Json::Value&
value)
337 return m_set(m_data, name(),
value);
344 const std::string& name()
const
346 return DatalessParameter::name;
348 const std::string& alias()
const
350 return DatalessParameter::alias;
352 const std::string& description()
const
354 return DatalessParameter::description;
356 bool is_required()
const
358 return DatalessParameter::is_required;
369 std::shared_ptr<void> m_data;
370 IOResult<Json::Value> (*m_serialize)(
371 const std::shared_ptr<void>&);
372 IOResult<void> (*m_set)(std::shared_ptr<void>&,
const std::string&,
379 using MapType = std::unordered_map<std::string_view, AbstractParameter&>;
391 static IOResult<AbstractSet> build(Set& parameter_set)
393 AbstractSet set(parameter_set);
394 BOOST_OUTCOME_TRY(fill_maps(set));
403 IOResult<Json::Value> get_param(
const Identifier&
id)
405 auto param = find(
id);
407 return mio::failure(param.error().code(),
"Could not get parameter: " + param.error().message());
410 return param.value()->second.get();
420 IOResult<void> set_param(
const Identifier&
id,
const std::string& args)
424 std::stringstream args_stream(args);
425 Json::parseFromStream(Json::CharReaderBuilder{}, args_stream, &js, &errors);
427 return set_param(
id, js, errors);
437 IOResult<void> set_param(
const Identifier&
id,
const Json::Value&
value,
const std::string& parse_errors =
"")
439 auto param = find(
id);
441 return mio::failure(param.error().code(),
"Could not set parameter: " + param.error().message());
445 IOResult<void> result = param.value()->second.set(
value);
448 param.value()->second.DatalessParameter::is_required =
false;
451 else if (parse_errors.empty()) {
457 return mio::failure(result.error().code(), result.error().message() +
"\n" + parse_errors);
465 return static_cast<bool>(find(
id));
472 std::vector<AbstractParameter>& parameters()
476 const std::vector<AbstractParameter>& parameters()
const
484 AbstractSet() =
default;
487 template <
class... T,
template <
class...>
class Set>
488 AbstractSet(Set<T...>& parameter_set)
489 : m_parameters(
std::vector{AbstractParameter(
mio::
Tag<T>{}, parameter_set.template get<T>())...})
494 AbstractSet(std::vector<AbstractParameter>& parameters)
495 : m_parameters(parameters)
505 IOResult<MapType::iterator> find(
const Identifier&
id)
507 auto param_itr = m_map_by_alias.find(
id.strip());
508 if (param_itr != m_map_by_alias.end()) {
511 param_itr = m_map_by_name.find(
id.strip());
512 if (param_itr != m_map_by_name.end()) {
524 static IOResult<void> fill_maps(AbstractSet& set)
526 for (AbstractParameter& p : set.m_parameters) {
527 if (p.name().empty()) {
530 if (!set.m_map_by_name.insert({p.name(), p}).second) {
532 "Found two instances of \"" +
535 if (!p.alias().empty() && !set.m_map_by_alias.insert({p.alias(), p}).second) {
537 "Found two instances of \"" +
544 std::vector<AbstractParameter> m_parameters;
545 MapType m_map_by_name;
546 MapType m_map_by_alias;
557 void write_help(
const std::string& executable_name,
const AbstractSet& set,
558 const std::vector<std::string>& default_options, std::ostream& os);
564 mio::IOResult<void> command_line_interface(
const std::string& executable_name,
const std::span<char*>& argv,
565 cli::details::AbstractSet& parameters,
566 const std::vector<std::string>& default_options);
569 template <StringLiteral>
576 template <
class... Tags>
579 using Names = TypeList<type_at_index_t<0, Tags>...>;
580 using Types = TypeList<type_at_index_t<1, Tags>...>;
587 ParameterSet(std::vector<details::AbstractParameter>&& parameters)
588 : m_parameters(
std::move(parameters))
597 template <mio::StringLiteral Name>
600 constexpr
size_t parameter_index = index_of_type_v<details::NamedType<Name>, Names>;
601 using ReturnType = type_at_index_t<parameter_index, Types>;
602 return *
static_cast<ReturnType*
>(m_parameters[parameter_index].data());
611 inline details::AbstractParameter&
get()
614 return m_parameters[parameter_index];
618 std::vector<details::AbstractParameter> m_parameters;
626 mio::IOResult<void> write_abstract_set_to_file(mio::cli::details::AbstractSet& set,
const std::string& filepath);
633 mio::IOResult<void> read_abstract_set_from_file(mio::cli::details::AbstractSet& set,
const std::string& filepath);
638 template <
class... Params>
639 class ParameterSetBuilder
643 friend class ParameterSetBuilder;
647 ParameterSetBuilder() =
default;
663 template <mio::StringLiteral Name,
class Type>
664 [[nodiscard]]
inline auto add(Type&& initial_value, details::OptionalFields&& optionals = {}) &&
666 using ValueType = std::decay_t<Type>;
668 auto new_params = std::move(m_parameters);
670 std::shared_ptr<void> data(
new ValueType(std::forward<Type>(initial_value)), std::default_delete<ValueType>{});
672 new_params.emplace_back(Tag<ValueType>{},
673 details::DatalessParameter{.name = std::string(Name),
674 .alias = std::move(optionals).alias,
675 .description = std::move(optionals).description,
676 .is_required = std::move(optionals).is_required},
678 return ParameterSetBuilder<Params..., TypeList<details::NamedType<Name>, ValueType>>{std::move(new_params)};
682 [[nodiscard]]
inline mio::cli::details::ParameterSet<Params...> build() &&
684 return mio::cli::details::ParameterSet<Params...>(std::move(m_parameters));
689 ParameterSetBuilder(std::vector<details::AbstractParameter>&& parameters)
690 : m_parameters(
std::move(parameters))
694 std::vector<details::AbstractParameter> m_parameters;
706 IOResult<void> write_parameters_to_file(Set& parameters,
const std::string& filepath)
708 BOOST_OUTCOME_TRY(
auto&& set, cli::details::AbstractSet::build(parameters));
709 return write_abstract_set_to_file(set, filepath);
719 template <
class... Parameters,
template <
class...>
class Set>
720 IOResult<void> read_parameters_from_file(Set<Parameters...>& parameters,
const std::string& filepath)
722 BOOST_OUTCOME_TRY(
auto&& set, cli::details::AbstractSet::build(parameters));
723 return read_abstract_set_from_file(set, filepath);
762 mio::IOResult<void> command_line_interface(
const std::string& executable_name,
const int argc,
char** argv,
763 Set& parameters,
const std::vector<std::string>& default_options = {})
766 BOOST_OUTCOME_TRY(
auto&& set, cli::details::AbstractSet::build(parameters));
767 return cli::details::command_line_interface(executable_name, std::span(argv, argc), set, default_options);
783 template <
class... Parameters,
template <
class...>
class Set =
mio::ParameterSet>
784 mio::IOResult<Set<Parameters...>> command_line_interface(
const std::string& executable_name,
const int argc,
786 const std::vector<std::string>& default_options = {})
788 static_assert(
sizeof...(Parameters) != 0,
"At least one Parameter is required.");
790 Set<Parameters...> parameters{};
791 auto result = command_line_interface(executable_name, argc, argv, parameters, default_options);
a set of parameters defined at compile time
Definition: parameter_set.h:205
trait_value< T >::RETURN_TYPE & value(T &x)
Definition: ad.hpp:3308
A collection of classes to simplify handling of matrix shapes in meta programming.
Definition: models/abm/analyze_result.h:30
constexpr std::size_t index_of_type_v
The index of Type in the list Types.
Definition: metaprogramming.h:191
auto failure(const IOStatus &s)
Create an object that is implicitly convertible to an error IOResult<T>.
Definition: io.h:380
boost::outcome_v2::in_place_type_t< T > Tag
Type that is used for overload resolution.
Definition: io.h:407
requires(!std::is_trivial_v< T >) void BinarySerializerObject
Definition: binary_serializer.h:333
auto success()
Create an object that is implicitly convertible to a succesful IOResult<void>.
Definition: io.h:359
void unused(T &&...)
Does nothing, can be used to mark variables as not used.
Definition: compiler_diagnostics.h:30
constexpr std::tuple_element< I, std::tuple< Index< CategoryTags >... > >::type & get(Index< CategoryTags... > &i) noexcept
Retrieves the Index (by reference) at the Ith position of a MultiIndex.
Definition: index.h:294
boost::outcome_v2::unchecked< T, IOStatus > IOResult
Value-or-error type for operations that return a value but can fail.
Definition: io.h:353
bool contains(Iter b, Iter e, Pred p)
checks if there is an element in this range that matches a predicate
Definition: stl_util.h:301