Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Rewrite/simplify the C++ flag declaration
[simgrid.git] / include / xbt / config.hpp
1 /* Copyright (c) 2016. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 #ifndef _XBT_CONFIG_HPP_
8 #define _XBT_CONFIG_HPP_
9
10 #include <cstdlib>
11
12 #include <functional>
13 #include <stdexcept>
14 #include <string>
15 #include <type_traits>
16 #include <utility>
17
18 #include <xbt/base.h>
19 #include <xbt/config.h>
20
21 namespace simgrid {
22 namespace config {
23
24 bool parseBool(const char* value);
25 double parseDouble(const char* value);
26 long int parseLong(const char* value);
27
28 template<class T> struct parse_option {
29   static inline T parse(const char* value)
30   {
31     return T(value);
32   }
33 };
34
35 template<> struct parse_option<std::string> {
36   static inline std::string parse(const char* value)
37   {
38     return std::string(value);
39   }
40 };
41
42 template<>
43 struct parse_option<double> {
44   static inline double parse(const char* value)
45   {
46     return parseDouble(value);
47   }
48 };
49
50 template<>
51 struct parse_option<int> {
52   static inline double parse(const char* value)
53   {
54     return parseLong(value);
55   }
56 };
57
58 template<>
59 struct parse_option<bool> {
60   static inline bool parse(const char* value)
61   {
62     return parseBool(value);
63   }
64 };
65
66 template<class T> inline
67 T parse(const char* value)
68 {
69   return parse_option<T>::parse(value);
70 }
71
72 template<class T> inline
73 std::string to_string(T&& value)
74 {
75   return std::to_string(std::forward<T>(value));
76 }
77 inline std::string const& to_string(std::string& value)
78 {
79   return value;
80 }
81 inline std::string const& to_string(std::string const& value)
82 {
83   return value;
84 }
85 inline std::string to_string(std::string&& value)
86 {
87   return std::move(value);
88 }
89
90 // Register:
91
92 /** Register a configuration flag
93  *
94  *  @param name        name of the option
95  *  @param description Description of the option
96  *  @param callback    called with the option value
97  */
98 XBT_PUBLIC(void) declareFlag(const char* name, const char* description,
99   std::function<void(const char* value)> callback);
100
101 /** Bind a variable to configuration flag
102  *
103  *  @param value Bound variable
104  *  @param name  Flag name
105  *  @param description Option description
106  */
107 template<class T>
108 void bindFlag(T& value, const char* name, const char* description)
109 {
110   using namespace std;
111   declareFlag(name, description, [&value](const char* val) {
112     value = simgrid::config::parse<T>(val);
113   });
114   xbt_cfg_setdefault_string(name, simgrid::config::to_string(value).c_str());
115 }
116
117 /** Bind a variable to configuration flag
118  *
119  *  <pre><code>
120  *  static int x;
121  *  simgrid::config::bindFlag(a, "x", [](const char* value) {
122  *    return simgrid::config::parse(value);
123  *  }
124  *  </pre><code>
125  */
126 // F is a parser, F : const char* -> T
127 template<class T, class F>
128 typename std::enable_if<std::is_same<
129   T,
130   typename std::remove_cv< decltype(
131     std::declval<F>()(std::declval<const char*>())
132   ) >::type
133 >::value, void>::type
134 bindFlag(T& value, const char* name, const char* description,
135   F callback)
136 {
137   declareFlag(name, description, [&value,callback](const char* val) {
138     value = callback(val);
139   });
140   xbt_cfg_setdefault_string(name, to_string(value).c_str());
141 }
142
143 /** Bind a variable to configuration flag
144  *
145  *  <pre><code>
146  *  static int x;
147  *  simgrid::config::bindFlag(a, "x", [](int x) {
148  *    if (x < x_min || x => x_max)
149  *      throw std::range_error("must be in [x_min, x_max)")
150  *  });
151  *  </pre><code>
152  */
153 // F is a checker, F : T& -> ()
154 template<class T, class F>
155 typename std::enable_if<std::is_same<
156   void,
157   decltype( std::declval<F>()(std::declval<const T&>()) )
158 >::value, void>::type
159 bindFlag(T& value, const char* name, const char* description,
160   F callback)
161 {
162   declareFlag(name, description, [&value,callback](const char* val) {
163     T res = parse<T>(val);
164     callback(res);
165     value = std::move(res);
166   });
167   xbt_cfg_setdefault_string(name, to_string(value).c_str());
168 }
169
170 /** Bind a variable to configuration flag
171  *
172  *  <pre><code>
173  *  static int x;
174  *  simgrid::config::bindFlag(a, "x", [](int x) { return return x > 0; });
175  *  </pre><code>
176  */
177 // F is a predicate, F : T const& -> bool
178 template<class T, class F>
179 typename std::enable_if<std::is_same<
180   bool,
181   decltype( std::declval<F>()(std::declval<const T&>()) )
182 >::value, void>::type
183 bindFlag(T& value, const char* name, const char* description,
184   F callback)
185 {
186   declareFlag(name, description, [&value,callback](const char* val) {
187     T res = parse<T>(val);
188     if (!callback(res))
189       throw std::range_error("invalid value");
190     value = std::move(res);
191   });
192   xbt_cfg_setdefault_string(name, to_string(value).c_str());
193 }
194
195 /** A variable bound to a CLI option
196  *
197  *  <pre><code>
198  *  static simgrid::config::flag<int> answer("answer", "Expected answer", 42);
199  *  static simgrid::config::flag<std::string> name("name", "Ford Prefect", "John Doe");
200  *  static simgrid::config::flag<double> gamma("gamma", "Gamma factor", 1.987);
201  *  </code></pre>
202  */
203 template<class T>
204 class Flag {
205   T value_;
206 public:
207
208   /** Constructor
209    *
210    *  @param name  Flag name
211    *  @param desc  Flag description
212    *  @param value Flag initial/default value
213    */
214   Flag(const char* name, const char* desc, T value) : value_(value)
215   {
216     simgrid::config::bindFlag(value_, name, desc);
217   }
218
219   template<class F>
220   Flag(const char* name, const char* desc, T value, F callback) : value_(value)
221   {
222     simgrid::config::bindFlag(value_, name, desc, std::move(callback));
223   }
224
225   // No copy:
226   Flag(Flag const&) = delete;
227   Flag& operator=(Flag const&) = delete;
228
229   // Get the underlying value:
230   T& get() { return value_; }
231   T const& get() const { return value_; }
232
233   // Implicit conversion to the underlying type:
234   operator T&() { return value_; }
235   operator T const&() const{ return value_; }
236
237   // Basic interop with T:
238   Flag& operator=(T const& that) { value_ = that; return *this; }
239   Flag& operator=(T && that)     { value_ = that; return *this; }
240   bool operator==(T const& that) const { return value_ == that; }
241   bool operator!=(T const& that) const { return value_ != that; }
242   bool operator<(T const& that) const { return value_ < that; }
243   bool operator>(T const& that) const { return value_ > that; }
244   bool operator<=(T const& that) const { return value_ <= that; }
245   bool operator>=(T const& that) const { return value_ >= that; }
246 };
247
248 }
249 }
250
251 #endif