R3BROOT
R3B analysis software
Loading...
Searching...
No Matches
R3BProgramOptions.h
Go to the documentation of this file.
1#pragma once
2
3#include <boost/program_options.hpp>
4#include <fmt/format.h>
5#include <iostream>
6#include <optional>
7#include <unordered_map>
8#include <utility>
9
10namespace R3B
11{
12 namespace po = boost::program_options;
13
14 // TODO: C++20 use concepts
15 template <typename>
16 struct is_bool : std::false_type
17 {
18 };
19
20 template <>
21 struct is_bool<bool> : std::true_type
22 {
23 };
24
25 template <typename T, typename = void>
26 struct IsString : std::false_type
27 {
28 };
29
30 template <typename T>
31 struct IsString<T, std::void_t<decltype(std::string_view{ std::declval<T>() })>> : std::true_type
32 {
33 };
34
35 template <typename T>
37
38 class OptionConcept;
39 template <typename Type>
40 class Option;
41
42 template <typename Type>
43 class OptionHandle;
44
46 {
47 public:
48 ProgramOptions() = default;
49 explicit ProgramOptions(const std::string& desc)
50 : desc_{ desc }
51 {
52 }
53
54 template <typename OptionType>
55 auto create_option(const std::string& optionName,
56 const std::string& option_desc,
57 std::optional<OptionType> defaultValue = {}) -> decltype(auto)
58 {
59 if (auto search = registries_.find(optionName); search != registries_.end())
60 {
61 std::cerr << "ERROR: option has been already defined!" << std::endl;
62 exit(1);
63 }
64 auto option = std::make_unique<Option<OptionType>>(optionName, defaultValue.value_or(OptionType{}), this);
65 option->add(option_desc, not defaultValue.has_value());
66 registries_.emplace(optionName, option.get());
67 return OptionHandle{ std::move(option) };
68 }
69
70 auto verify(int argc, char** argv) -> bool;
71 void delete_option(const std::string& optionName) { registries_.erase(optionName); }
72 auto get_posDescRef() -> auto& { return pos_desc_; }
73 auto get_desc_ref() -> auto& { return desc_; }
74
75 private:
76 std::unordered_map<std::string, OptionConcept*> registries_;
77 po::positional_options_description pos_desc_;
78 po::options_description desc_;
79 po::variables_map varMap_;
80 };
81
83 {
84 public:
85 OptionConcept(const OptionConcept&) = delete;
87 auto operator=(const OptionConcept&) -> OptionConcept& = delete;
89 virtual ~OptionConcept() = default;
90 OptionConcept() = default;
91 virtual void retrieve(const po::variables_map& varMap) = 0;
92 };
93
94 template <typename Type>
95 class Option : public OptionConcept
96 {
97 public:
98 using type = Type;
99 Option(const Option&) = delete;
100 Option(Option&&) = delete;
101 auto operator=(const Option&) -> Option& = delete;
102 auto operator=(Option&&) -> Option& = delete;
103 Option(std::string name, Type defaultValue, ProgramOptions* program)
104 : name_{ std::move(name) }
105 , value_{ std::move(defaultValue) }
106 , program_{ program }
107 {
108 if (auto end = name_.find(','))
109 {
110 key_ = name_.substr(0, end);
111 }
112 else
113 {
114 key_ = name_;
115 }
116 }
117 ~Option() override { program_->delete_option(name_); }
118
119 void add(const std::string& desc, bool is_requried = false)
120 {
121 auto& po_desc = program_->get_desc_ref();
122 is_required_ = is_requried;
123
124 auto desc_full = fmt::format("{} [ required ]", desc);
125 if (not is_required_)
126 {
127 desc_full = fmt::format("{} [ = {} ]", desc, value_);
128 if constexpr (IsString_v<Type>)
129 {
130 desc_full = fmt::format("{} [ = {:?} ]", desc, value_);
131 }
132 }
133
134 if constexpr (is_bool<Type>::value)
135 {
136 po_desc.add_options()(name_.c_str(), desc_full.c_str());
137 }
138 else
139 {
140 po_desc.add_options()(name_.c_str(), po::value<Type>(), desc_full.c_str());
141 }
142 }
143
144 void as_positional(int option)
145 {
146 program_->get_posDescRef().add(key_.c_str(), option);
147 is_positional_ = true;
148 }
149
150 void retrieve(const po::variables_map& varMap) override
151 {
152 if (varMap.count(key_) != 0U)
153 {
154
155 if constexpr (is_bool<Type>::value)
156 {
157 value_ = true;
158 }
159 else
160 {
161 value_ = varMap[key_].template as<Type>();
162 }
163 }
164 else if (is_required_)
165 {
166 const auto error_msg = fmt::format(R"(Program option "--{}" is required! )", name_);
167 throw std::runtime_error(error_msg);
168 }
169 }
170
171 void set_required(bool p_rq = true) { is_required_ = p_rq; }
172
173 [[nodiscard]] auto value() const { return value_; }
174 [[nodiscard]] auto is_required() const -> bool { return is_required_; }
175 [[nodiscard]] auto is_positional() const -> bool { return is_positional_; }
176
177 private:
178 bool is_positional_ = false;
179 bool is_required_ = false;
180 std::string name_;
181 std::string key_;
182 std::string desc_;
183 Type value_{};
184 ProgramOptions* program_;
185 };
186
187 template <typename Type>
189 {
190 public:
191 explicit OptionHandle(std::unique_ptr<Option<Type>> option)
192 : option_{ std::move(option) }
193 {
194 }
195
196 auto value() const { return option_->value(); }
197 auto operator()() const { return option_->value(); }
199 {
200 option_->as_positional(pos);
201 return std::move(*this);
202 }
203 auto operator->() -> Option<Type>* { return option_.get(); }
204
205 private:
206 std::unique_ptr<Option<Type>> option_ = nullptr;
207 };
208
209}; // namespace R3B
auto operator=(const OptionConcept &) -> OptionConcept &=delete
virtual ~OptionConcept()=default
OptionConcept()=default
OptionConcept(OptionConcept &&)=delete
OptionConcept(const OptionConcept &)=delete
virtual void retrieve(const po::variables_map &varMap)=0
auto operator=(OptionConcept &&) -> OptionConcept &=delete
auto operator->() -> Option< Type > *
auto make_positional(int pos) -> OptionHandle< Type > &&
OptionHandle(std::unique_ptr< Option< Type > > option)
~Option() override
auto value() const
void add(const std::string &desc, bool is_requried=false)
Option(const Option &)=delete
auto is_positional() const -> bool
auto operator=(Option &&) -> Option &=delete
void set_required(bool p_rq=true)
auto operator=(const Option &) -> Option &=delete
auto is_required() const -> bool
Option(Option &&)=delete
void retrieve(const po::variables_map &varMap) override
void as_positional(int option)
Option(std::string name, Type defaultValue, ProgramOptions *program)
auto get_desc_ref() -> auto &
ProgramOptions(const std::string &desc)
ProgramOptions()=default
auto get_posDescRef() -> auto &
auto verify(int argc, char **argv) -> bool
void delete_option(const std::string &optionName)
auto create_option(const std::string &optionName, const std::string &option_desc, std::optional< OptionType > defaultValue={}) -> decltype(auto)
constexpr bool IsString_v