Vowpal Wabbit
options_boost_po.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <boost/program_options.hpp>
4 namespace po = boost::program_options;
5 
6 #include <memory>
7 #include <vector>
8 #include <sstream>
9 #include <set>
10 #include <algorithm>
11 
12 #include "options.h"
13 #include "options_types.h"
14 #include "vw_exception.h"
15 
16 // Boost Program Options requires that all types that have a default option are ostreamable
17 namespace std
18 {
19 template <typename T>
20 std::ostream& operator<<(std::ostream& os, const std::vector<T>& vec)
21 {
22  for (auto const& item : vec)
23  {
24  os << item << ", ";
25  }
26  return os;
27 }
28 } // namespace std
29 
30 template std::ostream& std::operator<<<int>(std::ostream&, const std::vector<int>&);
31 template std::ostream& std::operator<<<char>(std::ostream&, const std::vector<char>&);
32 template std::ostream& std::operator<<<std::string>(std::ostream&, const std::vector<std::string>&);
33 template std::ostream& std::operator<<<float>(std::ostream&, const std::vector<float>&);
34 template std::ostream& std::operator<<<bool>(std::ostream&, const std::vector<bool>&);
35 
36 namespace VW
37 {
38 namespace config
39 {
40 struct options_boost_po : public options_i
41 {
42  options_boost_po(int argc, char** argv) : options_boost_po(std::vector<std::string>(argv + 1, argv + argc)) {}
43 
44  options_boost_po(std::vector<std::string> args) : m_command_line(args) {}
45 
47  options_boost_po& operator=(options_boost_po&) = delete;
48 
49  virtual void add_and_parse(const option_group_definition& group) override;
50  virtual bool was_supplied(const std::string& key) override;
51  virtual std::string help() override;
52  virtual void check_unregistered() override;
53  virtual std::vector<std::shared_ptr<base_option>> get_all_options() override;
54  virtual std::shared_ptr<base_option> get_option(const std::string& key) override;
55 
56  virtual void insert(const std::string& key, const std::string& value) override
57  {
58  m_command_line.push_back("--" + key);
59  if (value != "")
60  {
61  m_command_line.push_back(value);
62  }
63  }
64 
65  // Note: does not work for vector options.
66  virtual void replace(const std::string& key, const std::string& value) override
67  {
68  auto full_key = "--" + key;
69  auto it = std::find(m_command_line.begin(), m_command_line.end(), full_key);
70 
71  // Not found, insert instead.
72  if (it == m_command_line.end())
73  {
74  insert(key, value);
75  return;
76  }
77 
78  // Check if it is the final option or the next option is not a value.
79  if (it + 1 == m_command_line.end() || (*(it + 1)).find("--") != std::string::npos)
80  {
81  THROW(key + " option does not have a value.");
82  }
83 
84  // Actually replace the value.
85  *(it + 1) = value;
86  }
87 
88  // key must reference an option previously defined.
89  bool try_get_positional_option_token(const std::string& key, std::string& token, int position)
90  {
91  po::positional_options_description p;
92  p.add(key.c_str(), position);
93  po::parsed_options pos = po::command_line_parser(m_command_line)
94  .style(po::command_line_style::default_style ^ po::command_line_style::allow_guessing)
95  .options(master_description)
96  .allow_unregistered()
97  .positional(p)
98  .run();
99 
100  auto it = std::find_if(pos.options.begin(), pos.options.end(),
101  [&key](boost::program_options::option& option) { return option.string_key == key; });
102 
103  if (it != pos.options.end() && (*it).value.size() > 0)
104  {
105  token = (*it).value.at(0);
106  return true;
107  }
108 
109  return false;
110  }
111 
112  private:
113  template <typename T>
114  typename po::typed_value<std::vector<T>>* get_base_boost_value(std::shared_ptr<typed_option<T>>& opt);
115 
116  template <typename T>
117  po::typed_value<std::vector<T>>* get_base_boost_value(std::shared_ptr<typed_option<std::vector<T>>>& opt);
118 
119  template <typename T>
120  po::typed_value<std::vector<T>>* convert_to_boost_value(std::shared_ptr<typed_option<T>>& opt);
121 
122  template <typename T>
123  po::typed_value<std::vector<T>>* convert_to_boost_value(std::shared_ptr<typed_option<std::vector<T>>>& opt);
124 
125  template <typename T>
126  po::typed_value<std::vector<T>>* add_notifier(
127  std::shared_ptr<typed_option<T>>& opt, po::typed_value<std::vector<T>>* po_value);
128 
129  template <typename T>
130  po::typed_value<std::vector<T>>* add_notifier(
131  std::shared_ptr<typed_option<std::vector<T>>>& opt, po::typed_value<std::vector<T>>* po_value);
132 
133  template <typename T>
134  bool add_if_t(std::shared_ptr<base_option> opt, po::options_description& options_description);
135 
136  void add_to_description(std::shared_ptr<base_option> opt, po::options_description& options_description);
137 
138  template <typename TTypes>
139  void add_to_description_impl(std::shared_ptr<base_option> opt, po::options_description& options_description)
140  {
141  if (add_if_t<typename TTypes::head>(opt, options_description))
142  {
143  return;
144  }
145  add_to_description_impl<typename TTypes::tail>(opt, options_description);
146  }
147 
148  template <typename T>
149  void add_to_description(std::shared_ptr<typed_option<T>> opt, po::options_description& options_description);
150 
151  private:
152  std::map<std::string, std::shared_ptr<base_option>> m_options;
153 
154  std::vector<std::string> m_command_line;
155 
156  std::stringstream m_help_stringstream;
157 
158  // All options that were supplied on the command line.
159  std::set<std::string> m_supplied_options;
160 
161  // Used the ignore values that get incorrectly interpreted as options.
162  std::set<std::string> m_ignore_supplied;
163 
164  po::options_description master_description;
165 
166  // All options that a description was provided for.
167  std::set<std::string> m_defined_options;
168 };
169 
170 template <typename T>
171 po::typed_value<std::vector<T>>* options_boost_po::get_base_boost_value(std::shared_ptr<typed_option<T>>& opt)
172 {
173  auto value = po::value<std::vector<T>>();
174 
175  if (opt->default_value_supplied())
176  {
177  value->default_value({opt->default_value()});
178  }
179 
180  return add_notifier(opt, value)->composing();
181 }
182 
183 template <typename T>
184 po::typed_value<std::vector<T>>* options_boost_po::get_base_boost_value(
185  std::shared_ptr<typed_option<std::vector<T>>>& opt)
186 {
187  auto value = po::value<std::vector<T>>();
188 
189  if (opt->default_value_supplied())
190  {
191  value->default_value(opt->default_value());
192  }
193 
194  return add_notifier(opt, value)->composing();
195 }
196 
197 template <typename T>
198 po::typed_value<std::vector<T>>* options_boost_po::convert_to_boost_value(std::shared_ptr<typed_option<T>>& opt)
199 {
200  return get_base_boost_value(opt);
201 }
202 
203 template <typename T>
204 po::typed_value<std::vector<T>>* options_boost_po::convert_to_boost_value(
205  std::shared_ptr<typed_option<std::vector<T>>>& opt)
206 {
207  return get_base_boost_value(opt)->multitoken();
208 }
209 
210 template <>
211 po::typed_value<std::vector<bool>>* options_boost_po::convert_to_boost_value(std::shared_ptr<typed_option<bool>>& opt);
212 
213 template <typename T>
214 po::typed_value<std::vector<T>>* options_boost_po::add_notifier(
215  std::shared_ptr<typed_option<T>>& opt, po::typed_value<std::vector<T>>* po_value)
216 {
217  return po_value->notifier([opt](std::vector<T> final_arguments) {
218  T first = final_arguments[0];
219  for (auto const& item : final_arguments)
220  {
221  if (item != first)
222  {
223  std::stringstream ss;
224  ss << "Disagreeing option values for '" << opt->m_name << "': '" << first << "' vs '" << item << "'";
226  }
227  }
228 
229  // Set the value for the listening location.
230  opt->m_location = first;
231  opt->value(first);
232  });
233 }
234 
235 template <typename T>
236 po::typed_value<std::vector<T>>* options_boost_po::add_notifier(
237  std::shared_ptr<typed_option<std::vector<T>>>& opt, po::typed_value<std::vector<T>>* po_value)
238 {
239  return po_value->notifier([opt](std::vector<T> final_arguments) {
240  // Set the value for the listening location.
241  opt->m_location = final_arguments;
242  opt->value(final_arguments);
243  });
244 }
245 
246 template <typename T>
247 bool options_boost_po::add_if_t(std::shared_ptr<base_option> opt, po::options_description& options_description)
248 {
249  if (opt->m_type_hash == typeid(T).hash_code())
250  {
251  auto typed = std::dynamic_pointer_cast<typed_option<T>>(opt);
252  add_to_description(typed, options_description);
253  return true;
254  }
255 
256  return false;
257 }
258 
259 template <>
260 void options_boost_po::add_to_description_impl<typelist<>>(
261  std::shared_ptr<base_option> opt, po::options_description& options_description);
262 
263 template <typename T>
264 void options_boost_po::add_to_description(
265  std::shared_ptr<typed_option<T>> opt, po::options_description& options_description)
266 {
267  std::string boost_option_name = opt->m_name;
268  if (opt->m_short_name != "")
269  {
270  boost_option_name += ",";
271  boost_option_name += opt->m_short_name;
272  }
273  options_description.add_options()(boost_option_name.c_str(), convert_to_boost_value(opt), opt->m_help.c_str());
274 
275  if (m_defined_options.count(opt->m_name) == 0)
276  {
277  // TODO may need to add noop notifier here.
278  master_description.add_options()(boost_option_name.c_str(), convert_to_boost_value(opt), "");
279  }
280 }
281 } // namespace config
282 } // namespace VW
#define THROW_EX(ex, args)
Definition: vw_exception.h:188
std::vector< std::string > m_command_line
bool try_get_positional_option_token(const std::string &key, std::string &token, int position)
void add_to_description_impl(std::shared_ptr< base_option > opt, po::options_description &options_description)
po::options_description master_description
std::set< std::string > m_ignore_supplied
options_boost_po(std::vector< std::string > args)
virtual void insert(const std::string &key, const std::string &value) override
options_boost_po(int argc, char **argv)
std::set< std::string > m_supplied_options
node_pred * find(recall_tree &b, uint32_t cn, example &ec)
Definition: recall_tree.cc:126
std::map< std::string, std::shared_ptr< base_option > > m_options
Definition: autolink.cc:11
std::set< std::string > m_defined_options
std::stringstream m_help_stringstream
virtual void replace(const std::string &key, const std::string &value) override
#define THROW(args)
Definition: vw_exception.h:181