Toolbox snapshot
The Reactive C++ Toolbox
Loading...
Searching...
No Matches
Options.hpp
Go to the documentation of this file.
1// The Reactive C++ Toolbox.
2// Copyright (C) 2013-2019 Swirly Cloud Limited
3// Copyright (C) 2022 Reactive Markets Limited
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17#ifndef TOOLBOX_UTIL_OPTIONS_HPP
18#define TOOLBOX_UTIL_OPTIONS_HPP
19
27
31
32#include <map>
33#include <variant>
34#include <format>
35
36namespace toolbox {
37inline namespace util {
38
39class NoOp {};
40class Help {};
41
42template <typename DerivedT>
43class Presence {
44 public:
46
48 {
49 presence_ = Required;
50 return *static_cast<DerivedT*>(this);
51 }
52
53 Type presence() const noexcept { return presence_; }
54
55 private:
56 Type presence_{Optional};
57};
58
59class Value : public Presence<Value> {
60
61 public:
62 template <typename VarT>
63 explicit Value(VarT& var)
64 : func_{[&var](std::string_view arg) { var = from_string<VarT>(arg); }}
65 {
66 }
67
68 template <typename VarT, typename AllocT>
69 explicit Value(std::vector<VarT, AllocT>& var)
70 : func_{[&var](std::string_view arg) { var.push_back(from_string<VarT>(arg)); }}
71 {
72 }
73
74 template <typename ValueT>
75 Value& default_value(const ValueT& value)
76 {
77 if constexpr (is_string_type_v<ValueT>) {
78 func_(value);
79 } else {
80 func_(std::to_string(value));
81 }
82 return *this;
83 }
85 {
86 multitoken_ = true;
87 return *this;
88 }
89
90 private:
91 friend class Options;
92
93 void run(std::string_view arg)
94 {
95 func_(arg);
96 set_ = true;
97 }
98
99 bool set() const noexcept { return set_; }
100
101 std::function<void(std::string_view)> func_;
102 bool set_{false};
103 bool multitoken_{false};
104};
105
106class Switch : public Presence<Switch> {
107 public:
108 Switch(bool& flag) // NOLINT(hicpp-explicit-conversions)
109 : flag_{flag}
110 {
111 }
112
113 private:
114 friend class Options;
115
116 void run() { flag_ = true; }
117
118 bool& flag_;
119};
120
122
123 struct OptionCompare {
124 using is_transparent = void;
125 bool operator()(const std::string& lhs, const std::string& rhs) const noexcept
126 {
127 return lhs < rhs;
128 }
129 bool operator()(const std::string& lhs, std::string_view rhs) const noexcept
130 {
131 return lhs < rhs;
132 }
133 bool operator()(std::string_view lhs, const std::string& rhs) const noexcept
134 {
135 return lhs < rhs;
136 }
137 };
138
139 using Data = std::variant<Value, Help, NoOp, Switch>;
140
141 struct OptionData : RefCount<OptionData, ThreadUnsafePolicy> {
142 template <typename DataT>
143 OptionData(char short_opt, const std::string& long_opt, DataT data, std::string description)
144 : long_opt{long_opt}
145 , data{std::move(data)}
146 , description{std::move(description)}
147 {
148 if (short_opt) {
149 this->short_opt.push_back(short_opt);
150 }
151 }
152 const std::string& opt() const noexcept { return long_opt.empty() ? short_opt : long_opt; }
153
154 std::string short_opt, long_opt;
155 Data data;
156 std::string description;
157 };
158
159 using OptionDataPtr = boost::intrusive_ptr<OptionData>;
160
161 public:
162 explicit Options(std::string description = "");
163
164 template <typename DataT>
165 Options& operator()(const std::string& long_opt, DataT&& option_data,
166 std::string description = "")
167 {
168 this->operator()(0, long_opt, std::forward<DataT>(option_data), std::move(description));
169 return *this;
170 }
171
172 template <typename DataT>
173 Options& operator()(char short_opt, const std::string& long_opt, DataT&& data,
174 std::string description = "")
175 {
176 auto opt_data = make_intrusive<OptionData>(short_opt, long_opt, std::forward<DataT>(data),
177 std::move(description));
178 help_.push_back(opt_data);
179 if (short_opt) {
180 const bool inserted = opts_.emplace(opt_data->short_opt, opt_data).second;
181 if (!inserted) {
182 throw std::runtime_error{"attempting to register duplicate option "
183 + opt_data->short_opt};
184 }
185 }
186 if (!long_opt.empty()) {
187 const bool inserted = opts_.emplace(long_opt, opt_data).second;
188 if (!inserted) {
189 throw std::runtime_error{"attempting to register duplicate option " + long_opt};
190 }
191 }
192 return *this;
193 }
194
195 template <typename DataT>
196 Options& operator()(DataT data, std::string /*description*/ = "")
197 {
198 positional_.push_back(std::move(data));
199 return *this;
200 }
201
202 template <typename DataT>
203 Options& operator()(char short_opt, DataT&& option_data, std::string description = "")
204 {
205 this->operator()(short_opt, "", std::forward<DataT>(option_data), std::move(description));
206 return *this;
207 }
208
209 bool operator[](const std::string& long_opt) const noexcept;
210 bool operator[](char short_opt) const noexcept;
211 void parse(int argc, const char* const argv[]);
212
213 private:
214 template <typename StreamT>
215 requires Streamable<StreamT>
216 friend StreamT& operator<<(StreamT& out, const Options& options);
217
218 std::string description_;
219 using HelpVec = std::vector<OptionDataPtr>;
220 HelpVec help_;
221 using OptsMap = std::map<std::string, OptionDataPtr, OptionCompare>;
222 OptsMap opts_;
223 using DataVec = std::vector<Data>;
224 DataVec positional_;
225};
226
227template <typename StreamT>
228 requires Streamable<StreamT>
229StreamT& operator<<(StreamT& out, const Options& options)
230{
231 out << "Usage: " << options.description_ << "\nOptions:\n";
232
233 for (const auto& opt : options.help_) {
234 unsigned max_width{15};
235 // value find
236 out << " ";
237 if (!opt->short_opt.empty()) {
238 max_width -= 2;
239 out << '-' << opt->short_opt;
240 }
241 if (!opt->long_opt.empty()) {
242 if (!opt->short_opt.empty()) {
243 max_width -= 2;
244 out << ", ";
245 }
246 max_width -= 2 + opt->long_opt.size();
247 out << "--" << opt->long_opt;
248 }
249 out << std::format("{:{}}", ' ', max_width) << opt->description << '\n';
250 }
251 return out;
252}
253
254} // namespace util
255} // namespace toolbox
256
257#endif // TOOLBOX_UTIL_OPTIONS_HPP
#define TOOLBOX_API
Definition Config.h:39
StreamT & operator<<(StreamT &os, const sockaddr_in &sa)
Definition Endpoint.hpp:164
Options & operator()(const std::string &long_opt, DataT &&option_data, std::string description="")
Definition Options.hpp:165
Options & operator()(DataT data, std::string="")
Definition Options.hpp:196
Options & operator()(char short_opt, const std::string &long_opt, DataT &&data, std::string description="")
Definition Options.hpp:173
Options & operator()(char short_opt, DataT &&option_data, std::string description="")
Definition Options.hpp:203
DerivedT & required()
Definition Options.hpp:47
Type presence() const noexcept
Definition Options.hpp:53
Base class for atomic referenced counted objects.
Definition RefCount.hpp:61
Value & multitoken()
Definition Options.hpp:84
Value(std::vector< VarT, AllocT > &var)
Definition Options.hpp:69
Value & default_value(const ValueT &value)
Definition Options.hpp:75
STL namespace.
const DataT & data(const MsgEvent &ev) noexcept
Definition Event.hpp:54
double var(const VarAccum &v) noexcept
Definition Math.hpp:109
constexpr auto bind() noexcept
Definition Slot.hpp:92