JayBeams  0.1
Another project to have fun coding.
config_recurse.hpp
Go to the documentation of this file.
1 #ifndef jb_config_recurse_hpp
2 #define jb_config_recurse_hpp
3 /**
4  * @file
5  *
6  * Breakout some of the helper classes from jb/config_object.hpp
7  *
8  * The definition of jb::config_object requires a number of helper
9  * classes and functions to recurse over compound configs
10  * (i.e. structs, vectors, pairs, etc.). The code in
11  * jb/config_object.hpp was getting too long to keep in a single file,
12  * but these classes and the code in jb/config_attribute.hpp are also
13  * deeply inter-related so they could not be simply refactored out.
14  * This is a signal of poor design, and I welcome suggestions on how
15  * to improve it. However, it works, and the user interface is really
16  * clean.
17  */
18 
19 #ifndef jb_config_attribute_hpp
20 #error "This file should only be included from jb/config_attribute.hpp"
21 #endif // !jb_config_attribute_hpp
22 
23 #include <sstream>
24 #include <type_traits>
25 
26 namespace jb {
27 
28 // Forward declarations
29 template <typename C, typename T>
30 class config_attribute;
31 
32 /**
33  * Recursively apply functions to config_object, attributes, and
34  * sequences of config objects.
35  *
36  * This is implemented as a struct with static functions (instead of
37  * standalone functions), so jb::config_object can forward declare the
38  * struct and grant 'friend' access to functions that should not be
39  * exposed otherwise.
40  */
42  /**
43  * Override a value with the configuration contained in a YAML::Node.
44  *
45  * This generic implementation is used by basic types (int, string,
46  * vector, etc). We provide partial specializations for all the
47  * interesting types (jb::config_attribute, jb::config_objects).
48  *
49  * The variadic template makes this the weakest match for resolution
50  * when compared to the other partial specializations.
51  */
52  template <typename T, typename... Args>
53  static void apply_overrides(
54  T& lhs, YAML::Node const& by_name, class_overrides const& by_class,
55  Args...) {
56  if (not by_name or not by_name.IsDefined() or by_name.IsNull()) {
57  // ... the node is not defined, nothing to override ...
58  return;
59  }
60  lhs = std::move(by_name.as<T>());
61  }
62 
63  /**
64  * Partial specialization for classes derived from jb::config_object.
65  */
66  template <typename T>
67  static typename std::enable_if<
68  std::is_base_of<config_object, T>::value, void>::type
70  T& lhs, YAML::Node const& by_name, class_overrides const& by_class) {
71  lhs.apply_overrides(by_name, by_class);
72  }
73 
74  /**
75  * Partial specialization for jb::config_attribute
76  */
77  template <typename C, typename T>
78  static void apply_overrides(
79  config_attribute<C, T>& lhs, YAML::Node const& by_name,
80  class_overrides const& by_class);
81 
82  /**
83  * Partial specialization for anything looking like a sequence of
84  * jb::config_object.
85  */
86  template <typename Sequence>
87  static typename std::enable_if<
88  std::is_base_of<config_object, typename Sequence::value_type>::value,
89  void>::type
91  Sequence& lhs, YAML::Node const& by_name,
92  class_overrides const& by_class) {
93  std::size_t idx = 0;
94  Sequence tmp;
95  for (auto i : by_name) {
96  typename Sequence::value_type v;
97  if (idx < lhs.size()) {
98  v = std::move(lhs[idx]);
99  }
100  apply_overrides(v, i, by_class);
101  tmp.push_back(std::move(v));
102  ++idx;
103  }
104  for (; idx < lhs.size(); ++idx) {
105  tmp.push_back(std::move(lhs[idx]));
106  }
107  lhs.swap(tmp);
108  }
109 
110  /**
111  * Implement the basic construct to create command-line options ased
112  * on a config_object.
113  */
114  template <typename T, typename... Args>
115  static void add_options(
116  boost::program_options::options_description& options, T const& object,
117  std::string const& prefix, config_object::attribute_descriptor const& d,
118  Args...) {
119  options.add_options()(
120  jb::config_object::cmdline_arg_name(prefix, d.name).c_str(),
121  boost::program_options::value<T>(), d.helpmsg.c_str());
122  }
123 
124  /**
125  * Partial specialization for classes derived from jb::config_object.
126  */
127  template <typename T>
128  static typename std::enable_if<
129  std::is_base_of<config_object, T>::value, void>::type
131  boost::program_options::options_description& options, T const& object,
132  std::string const& prefix, config_object::attribute_descriptor const& d) {
133  object.add_options(
134  options, jb::config_object::cmdline_arg_name(prefix, d.name), d);
135  }
136 
137  /**
138  * Partial specialization for configuration attribute.
139  */
140  template <typename C, typename T>
141  static void add_options(
142  boost::program_options::options_description& options,
143  config_attribute<C, T> const& object, std::string const& prefix,
145 
146  /**
147  * Partial specialization for anything looking like a sequence of
148  * jb::config_object.
149  */
150  template <typename Sequence>
151  static typename std::enable_if<
152  std::is_base_of<config_object, typename Sequence::value_type>::value,
153  void>::type
155  boost::program_options::options_description& options,
156  Sequence const& object, std::string const& prefix,
158  if (object.size() == 0) {
159  typedef typename Sequence::value_type child;
160  add_options(
161  options, child(), jb::config_object::cmdline_arg_name(prefix, d.name),
163  return;
164  }
165  int cnt = 0;
166  for (auto const& i : object) {
167  std::ostringstream os;
168  os << cnt;
169  add_options(
170  options, i, jb::config_object::cmdline_arg_name(prefix, d.name),
171  config_object::attribute_descriptor(os.str()).help(d.helpmsg));
172  cnt++;
173  }
174  }
175 
176  /**
177  * Partial specialization for things that look like pairs.
178  */
179  template <typename U, typename V>
180  static void add_options(
181  boost::program_options::options_description& options,
182  std::pair<U, V> const& object, std::string const& prefix,
184  add_options(
185  options, object.first,
188  d.helpmsg + ". Set the first field"));
189  add_options(
190  options, object.second,
193  d.helpmsg + ". Set the second field"));
194  }
195 
196  /**
197  * Get a value from the cmdline values and apply it to the object.
198  */
199  template <typename T, typename... Args>
200  static void apply_cmdline_values(
201  T& rhs, boost::program_options::variables_map const& vm,
202  std::string const& name, Args...) {
203  if (vm.count(name)) {
204  rhs = vm[name].as<T>();
205  }
206  }
207 
208  /**
209  * Partial specialization for jb::config_object and derived classes.
210  */
211  template <typename T>
212  static typename std::enable_if<
213  std::is_base_of<config_object, T>::value, void>::type
215  T& rhs, boost::program_options::variables_map const& vm,
216  std::string const& name) {
217  rhs.apply_cmdline_values(vm, name);
218  }
219 
220  /**
221  * Partial specialization for configuration attribute.
222  */
223  template <typename C, typename T>
224  static void apply_cmdline_values(
226  boost::program_options::variables_map const& vm, std::string const& name);
227 
228  /**
229  * Partial specialization for anything looking like a sequence of
230  * jb::config_object.
231  */
232  template <typename Sequence>
233  static typename std::enable_if<
234  std::is_base_of<config_object, typename Sequence::value_type>::value,
235  void>::type
237  Sequence& rhs, boost::program_options::variables_map const& vm,
238  std::string const& name) {
239  // TODO(#6)
240  if (rhs.size() == 0) {
241  rhs.resize(1);
242  }
243  int cnt = 0;
244  for (auto& i : rhs) {
245  std::ostringstream os;
246  os << cnt;
248  i, vm, jb::config_object::cmdline_arg_name(name, os.str()));
249  cnt++;
250  }
251  }
252 
253  /**
254  * Partial specialization for std::pair<>.
255  */
256  template <typename U, typename V>
257  static void apply_cmdline_values(
258  std::pair<U, V>& rhs, boost::program_options::variables_map const& vm,
259  std::string const& name) {
261  rhs.first, vm, jb::config_object::cmdline_arg_name(name, "first"));
263  rhs.second, vm, jb::config_object::cmdline_arg_name(name, "second"));
264  }
265 
266  /// Validate an arbitrary object
267  template <typename T, typename... Args>
268  static void validate(T const&, Args...) {
269  // do nothing, the validation for standalone attributes is usually
270  // implemented by the containing config_object
271  }
272 
273  /**
274  * Partial specialization of validate() for jb::config_object and
275  * derived classes.
276  */
277  template <typename T>
278  static typename std::enable_if<
279  std::is_base_of<config_object, T>::value, void>::type
280  validate(T const& object) {
281  object.validate_all();
282  }
283 
284  /**
285  * Partial of validate() for specialization for
286  * jb::config_attribute<>.
287  */
288  template <typename C, typename T>
289  static void validate(config_attribute<C, T> const& object);
290 
291  /**
292  * Partial specialization of validate() for anything looking
293  * like a sequence of jb::config_object.
294  */
295  template <typename Sequence>
296  static typename std::enable_if<
297  std::is_base_of<config_object, typename Sequence::value_type>::value,
298  void>::type
299  validate(Sequence const& seq) {
300  for (auto const& i : seq) {
301  validate(i);
302  }
303  }
304 
305  /**
306  * Partial specialization of validate() for std::pair<>.
307  */
308  template <typename U, typename V>
309  static void validate(std::pair<U, V> const& object) {
310  validate(object.first);
311  validate(object.second);
312  }
313 
314  /// Convert an arbitrary type to a YAML representation
315  template <typename T, typename... Args>
316  static YAML::Node to_yaml(T const& x, Args...) {
317  YAML::Node doc;
318  doc = x;
319  return doc;
320  }
321 
322  /**
323  * Partial specialization of to_yaml() for jb::config_object and
324  * derived classes.
325  */
326  template <typename T>
327  static typename std::enable_if<
328  std::is_base_of<config_object, T>::value, YAML::Node>::type
329  to_yaml(T const& object) {
330  return object.to_yaml();
331  }
332 
333  /**
334  * Partial of to_yaml() for specialization for
335  * jb::config_attribute<>.
336  */
337  template <typename C, typename T>
338  static YAML::Node to_yaml(config_attribute<C, T> const& object);
339 
340  /**
341  * Partial specialization of to_yaml() for anything looking
342  * like a sequence of jb::config_object.
343  */
344  template <typename Sequence>
345  static typename std::enable_if<
346  std::is_base_of<config_object, typename Sequence::value_type>::value,
347  YAML::Node>::type
348  to_yaml(Sequence const& seq) {
349  YAML::Node doc;
350  for (auto const& i : seq) {
351  doc.push_back(to_yaml(i));
352  }
353  return doc;
354  }
355 
356  /**
357  * Partial specialization of to_yaml() for std::pair<>.
358  */
359  template <typename U, typename V>
360  static YAML::Node to_yaml(std::pair<U, V> const& object) {
361  YAML::Node doc;
362  doc["first"] = object.first;
363  doc["second"] = object.second;
364  return doc;
365  }
366 };
367 } // namespace jb
368 
369 #endif // jb_config_recurse_hpp
static void validate(T const &, Args...)
Validate an arbitrary object.
static std::enable_if< std::is_base_of< config_object, typename Sequence::value_type >::value, void >::type validate(Sequence const &seq)
Partial specialization of validate() for anything looking like a sequence of jb::config_object.
static std::enable_if< std::is_base_of< config_object, T >::value, YAML::Node >::type to_yaml(T const &object)
Partial specialization of to_yaml() for jb::config_object and derived classes.
static void validate(std::pair< U, V > const &object)
Partial specialization of validate() for std::pair<>.
static std::enable_if< std::is_base_of< config_object, T >::value, void >::type apply_cmdline_values(T &rhs, boost::program_options::variables_map const &vm, std::string const &name)
Partial specialization for jb::config_object and derived classes.
static YAML::Node to_yaml(std::pair< U, V > const &object)
Partial specialization of to_yaml() for std::pair<>.
static std::enable_if< std::is_base_of< config_object, T >::value, void >::type add_options(boost::program_options::options_description &options, T const &object, std::string const &prefix, config_object::attribute_descriptor const &d)
Partial specialization for classes derived from jb::config_object.
std::map< std::string, YAML::Node > class_overrides
Store the overrides for each class.
Definition: merge_yaml.hpp:17
static std::enable_if< std::is_base_of< config_object, typename Sequence::value_type >::value, void >::type add_options(boost::program_options::options_description &options, Sequence const &object, std::string const &prefix, config_object::attribute_descriptor const &d)
Partial specialization for anything looking like a sequence of jb::config_object. ...
static void apply_cmdline_values(std::pair< U, V > &rhs, boost::program_options::variables_map const &vm, std::string const &name)
Partial specialization for std::pair<>.
static std::enable_if< std::is_base_of< config_object, T >::value, void >::type apply_overrides(T &lhs, YAML::Node const &by_name, class_overrides const &by_class)
Partial specialization for classes derived from jb::config_object.
Helper class to easily define configuration attributes.
static YAML::Node to_yaml(T const &x, Args...)
Convert an arbitrary type to a YAML representation.
static std::enable_if< std::is_base_of< config_object, typename Sequence::value_type >::value, YAML::Node >::type to_yaml(Sequence const &seq)
Partial specialization of to_yaml() for anything looking like a sequence of jb::config_object.
Recursively apply functions to config_object, attributes, and sequences of config objects...
static void apply_cmdline_values(T &rhs, boost::program_options::variables_map const &vm, std::string const &name, Args...)
Get a value from the cmdline values and apply it to the object.
static std::enable_if< std::is_base_of< config_object, typename Sequence::value_type >::value, void >::type apply_overrides(Sequence &lhs, YAML::Node const &by_name, class_overrides const &by_class)
Partial specialization for anything looking like a sequence of jb::config_object. ...
static std::enable_if< std::is_base_of< config_object, T >::value, void >::type validate(T const &object)
Partial specialization of validate() for jb::config_object and derived classes.
static void add_options(boost::program_options::options_description &options, std::pair< U, V > const &object, std::string const &prefix, config_object::attribute_descriptor const &d)
Partial specialization for things that look like pairs.
static void apply_overrides(T &lhs, YAML::Node const &by_name, class_overrides const &by_class, Args...)
Override a value with the configuration contained in a YAML::Node.
attribute_descriptor & help(std::string const &h)
static void add_options(boost::program_options::options_description &options, T const &object, std::string const &prefix, config_object::attribute_descriptor const &d, Args...)
Implement the basic construct to create command-line options ased on a config_object.
static std::string cmdline_arg_name(std::string const &prefix, std::string const &name)
Compute the full name of a command-line argument, given its prefix and short name.
static std::enable_if< std::is_base_of< config_object, typename Sequence::value_type >::value, void >::type apply_cmdline_values(Sequence &rhs, boost::program_options::variables_map const &vm, std::string const &name)
Partial specialization for anything looking like a sequence of jb::config_object. ...
The top-level namespace for the JayBeams library.
Definition: as_hhmmss.hpp:7