Vowpal Wabbit
options_boost_po.cc
Go to the documentation of this file.
1 #include "options_boost_po.h"
2 #include "parse_primitives.h"
3 
4 #include <sstream>
5 
6 #include <algorithm>
7 #include <iterator>
8 #include <utility>
9 
10 using namespace VW::config;
11 
12 bool is_number(const std::string& s)
13 {
14  substring ss = {const_cast<char*>(s.c_str()), const_cast<char*>(s.c_str()) + s.size()};
15  auto endptr = ss.end;
16  auto f = parseFloat(ss.begin, &endptr);
17  if ((endptr == ss.begin && ss.begin != ss.end) || std::isnan(f))
18  {
19  return false;
20  }
21 
22  return true;
23 }
24 
25 template <>
26 po::typed_value<std::vector<bool>>* options_boost_po::convert_to_boost_value(std::shared_ptr<typed_option<bool>>& opt)
27 {
28  auto value = get_base_boost_value(opt);
29 
30  if (opt->default_value_supplied())
31  {
32  THROW("Using a bool option type acts as a switch, no explicit default value is allowed.")
33  }
34 
35  value->default_value({false});
36  value->zero_tokens();
37  value->implicit_value({true});
38 
39  return add_notifier(opt, value);
40 }
41 
43  std::shared_ptr<base_option> opt, po::options_description& options_description)
44 {
45  add_to_description_impl<supported_options_types>(std::move(opt), options_description);
46 }
47 
49 {
50  po::options_description new_options(group.m_name);
51 
52  for (auto opt_ptr : group.m_options)
53  {
54  add_to_description(opt_ptr, new_options);
55  m_defined_options.insert(opt_ptr->m_name);
56  m_defined_options.insert(opt_ptr->m_short_name);
57  m_defined_options.insert("-" + opt_ptr->m_short_name);
58 
59  // The last definition is kept. There was a bug where using .insert at a later pointer changed the command line but
60  // the previously defined option's default value was serialized into the model. This resolves that state info.
61  m_options[opt_ptr->m_name] = opt_ptr;
62  }
63 
64  // Add the help for the given options.
65  new_options.print(m_help_stringstream);
66 
67  try
68  {
69  po::variables_map vm;
70  auto parsed_options = po::command_line_parser(m_command_line)
71  .options(new_options)
72  .style(po::command_line_style::default_style ^ po::command_line_style::allow_guessing)
73  .allow_unregistered()
74  .run();
75 
76  for (auto const& option : parsed_options.options)
77  {
78  // If the supplied option is interpreted as a number, then ignore it. There are no options like this and it is
79  // just a false positive.
80  if (is_number(option.string_key))
81  {
82  m_ignore_supplied.insert(option.string_key);
83  }
84 
85  m_supplied_options.insert(option.string_key);
86 
87  // If a std::string is later determined to be a value the erase it. This happens for negative numbers "-2"
88  for (auto& val : option.value)
89  {
90  m_ignore_supplied.insert(val);
91  }
92 
93  // Parsed options can contain short options in the form -k, we can only check these as the group definitions come
94  // in.
95  if (option.string_key.length() > 0 && option.string_key[0] == '-')
96  {
97  auto short_name = option.string_key.substr(1);
98  for (const auto& opt_ptr : group.m_options)
99  {
100  if (opt_ptr->m_short_name == short_name)
101  {
102  m_supplied_options.insert(short_name);
103  }
104  }
105  }
106  }
107 
108  po::store(parsed_options, vm);
109  po::notify(vm);
110  }
111  catch (boost::exception_detail::clone_impl<
112  boost::exception_detail::error_info_injector<boost::program_options::invalid_option_value>>& ex)
113  {
115  }
116  catch (boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::bad_lexical_cast>>& ex)
117  {
119  }
120  catch (boost::exception_detail::clone_impl<
121  boost::exception_detail::error_info_injector<boost::program_options::ambiguous_option>>& ex)
122  {
123  THROW(ex.what());
124  }
125  catch (boost::program_options::ambiguous_option& ex)
126  {
127  THROW(ex.what());
128  }
129 }
130 
131 bool options_boost_po::was_supplied(const std::string& key)
132 {
133  // Best check, only valid after options parsed.
134  if (m_supplied_options.count(key) > 0)
135  {
136  return true;
137  }
138 
139  // Basic check, std::string match against command line.
140  auto it = std::find(m_command_line.begin(), m_command_line.end(), std::string("--" + key));
141  return it != m_command_line.end();
142 }
143 
144 std::string options_boost_po::help() { return m_help_stringstream.str(); }
145 
146 std::vector<std::shared_ptr<base_option>> options_boost_po::get_all_options()
147 {
148  std::vector<std::shared_ptr<base_option>> output_values;
149 
150  std::transform(m_options.begin(), m_options.end(), std::back_inserter(output_values),
151  [](std::pair<const std::string, std::shared_ptr<base_option>>& kv) { return kv.second; });
152 
153  return output_values;
154 }
155 
156 std::shared_ptr<base_option> VW::config::options_boost_po::get_option(const std::string& key)
157 {
158  auto it = m_options.find(key);
159  if (it != m_options.end())
160  {
161  return it->second;
162  }
163 
164  throw std::out_of_range(key + " was not found.");
165 }
166 
167 // Check all supplied arguments against defined args.
169 {
170  for (auto const& supplied : m_supplied_options)
171  {
172  if (m_defined_options.count(supplied) == 0 && m_ignore_supplied.count(supplied) == 0)
173  {
174  THROW_EX(VW::vw_unrecognised_option_exception, "unrecognised option '--" << supplied << "'");
175  }
176  }
177 }
178 
179 template <>
180 void options_boost_po::add_to_description_impl<typelist<>>(std::shared_ptr<base_option>, po::options_description&)
181 {
182  THROW("That is an unsupported option type.");
183 }
po::typed_value< std::vector< T > > * add_notifier(std::shared_ptr< typed_option< T >> &opt, po::typed_value< std::vector< T >> *po_value)
po::typed_value< std::vector< T > > * convert_to_boost_value(std::shared_ptr< typed_option< T >> &opt)
char * end
Definition: hashstring.h:10
char * begin
Definition: hashstring.h:9
std::vector< std::shared_ptr< base_option > > m_options
Definition: options.h:104
#define THROW_EX(ex, args)
Definition: vw_exception.h:188
std::vector< std::string > m_command_line
const char * what() const noexcept override
Definition: vw_exception.cc:35
void add_to_description(std::shared_ptr< base_option > opt, po::options_description &options_description)
std::set< std::string > m_ignore_supplied
virtual void check_unregistered() override
bool is_number(const std::string &s)
virtual void add_and_parse(const option_group_definition &group) override
virtual std::vector< std::shared_ptr< base_option > > get_all_options() override
virtual bool was_supplied(const std::string &key) override
std::set< std::string > m_supplied_options
po::typed_value< std::vector< T > > * get_base_boost_value(std::shared_ptr< typed_option< T >> &opt)
virtual std::string help() override
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
std::set< std::string > m_defined_options
std::stringstream m_help_stringstream
float parseFloat(char *p, char **end, char *endLine=nullptr)
#define THROW(args)
Definition: vw_exception.h:181
virtual std::shared_ptr< base_option > get_option(const std::string &key) override
float f
Definition: cache.cc:40