Commit | Line | Data |
---|---|---|
0e23f538 TJ |
1 | /* |
2 | The software in this package is distributed under the GNU General | |
3 | Public License version 2 (with a special exception described below). | |
4 | ||
5 | A copy of GNU General Public License (GPL) is included in this distribution, | |
6 | in the file COPYING.GPL. | |
7 | ||
8 | As a special exception, if other files instantiate templates or use macros | |
9 | or inline functions from this file, or you compile this file and link it | |
10 | with other works to produce a work based on this file, this file | |
11 | does not by itself cause the resulting work to be covered | |
12 | by the GNU General Public License. | |
13 | ||
14 | However the source code for this file must still be made available | |
15 | in accordance with section (3) of the GNU General Public License. | |
16 | ||
17 | This exception does not invalidate any other reasons why a work based | |
18 | on this file might be covered by the GNU General Public License. | |
19 | */ | |
23d53eb1 RP |
20 | /** @file |
21 | * @brief provides config file reading and automatic propagation of the values. | |
22 | * | |
0e23f538 | 23 | * @author Reinhard Pfau |
23d53eb1 RP |
24 | * |
25 | * @copyright © Copyright 2008 by Intra2net AG | |
23d53eb1 RP |
26 | * |
27 | * Basic idea of this module is that one need only to declare its config vars of | |
28 | * the type Config::Var\< T \> and pass the desired section and key from the | |
29 | * (INI style) config file where the value for this var should be searched. | |
30 | * The string value from the config file is converted to the desired type using a | |
31 | * converter class (a default is provided and uses a conversion based on string_to template | |
32 | * from libi2ncommon). | |
33 | * | |
34 | * Populating the var is automagically done when the config is read or reread or when | |
35 | * the variable is defined. | |
36 | * | |
37 | * | |
38 | * @todo review. consolidate. beautify. | |
39 | * @todo the converter should be better named as "validator" since they do not only convert... | |
40 | * @todo support for std::set<> ? | |
41 | */ | |
42 | ||
43 | #ifndef __I2N_GLOBAL_CONFIG_HPP__ | |
44 | #define __I2N_GLOBAL_CONFIG_HPP__ | |
45 | ||
46 | #include <string> | |
47 | #include <functional> | |
48 | #include <vector> | |
49 | #include <list> | |
50 | #include <ostream> | |
51 | #include <sstream> | |
52 | #include <stringfunc.hxx> | |
53 | #include <boost/shared_ptr.hpp> | |
54 | #include <boost/static_assert.hpp> | |
55 | #include <boost/type_traits.hpp> | |
56 | #include <boost/function.hpp> | |
57 | #include <boost/bind.hpp> | |
58 | #include <boost/foreach.hpp> | |
59 | ||
60 | ||
61 | namespace I2n | |
62 | { | |
63 | ||
64 | namespace Config | |
65 | { | |
66 | ||
67 | template< | |
68 | typename ValueType, | |
69 | class Converter | |
70 | > | |
71 | class Var; | |
72 | ||
73 | ||
74 | typedef std::vector< std::string > StringList; | |
75 | ||
76 | /** | |
77 | * @brief contains the "magic" details of the config module | |
78 | * (i.e. all the stuff which must be declared here, but U shouldn't use it!) | |
79 | */ | |
80 | namespace Detail { | |
81 | ||
82 | ||
83 | typedef boost::function< bool(const std::string&) > FeedFunction; | |
84 | typedef boost::function< bool(const StringList&) > FeedFunction2; | |
85 | ||
86 | ||
87 | struct Thing | |
88 | { | |
89 | virtual ~Thing() {} | |
90 | }; // eo struct Thing | |
91 | ||
92 | typedef boost::shared_ptr< Thing > FlowerShop; | |
93 | ||
94 | ||
95 | ||
96 | ||
97 | FlowerShop seymour(const std::string& section, const std::string& key, const FeedFunction& feed); | |
98 | FlowerShop seymour(const std::string& section, const std::string& key, const FeedFunction2& feed); | |
99 | FlowerShop seymour(FlowerShop shop, const FeedFunction& feed); | |
100 | FlowerShop seymour(FlowerShop shop, const FeedFunction2& feed); | |
101 | ||
102 | /* | |
103 | ** type magics: | |
104 | */ | |
105 | ||
106 | /** | |
107 | * @brief determines if a type is one of the supported list containers. | |
108 | * @tparam Container the type to test for supported list type. | |
109 | * | |
110 | * currently we support std::list<> and std::vector<> (and derivates). | |
111 | */ | |
112 | template< class Container > | |
113 | struct is_list_container | |
114 | { | |
115 | static Container gen(); | |
116 | ||
117 | template< typename T > | |
118 | static boost::type_traits::yes_type c1( const std::vector< T >& ); | |
119 | ||
120 | template< typename T > | |
121 | static boost::type_traits::yes_type c1( const std::list< T >& ); | |
122 | ||
123 | static boost::type_traits::no_type c1(...); | |
124 | ||
125 | BOOST_STATIC_CONSTANT( bool, value= ( sizeof(c1(gen())) == sizeof(boost::type_traits::yes_type) ) ); | |
126 | }; | |
127 | ||
128 | ||
129 | template< class C, bool is_list > | |
130 | struct extract_type_impl | |
131 | { | |
132 | typedef typename boost::remove_reference< typename boost::remove_const< C >::type>::type type; | |
133 | }; | |
134 | ||
135 | template< class C > | |
136 | struct extract_type_impl< C, true > | |
137 | { | |
138 | typedef typename boost::remove_reference< | |
139 | typename boost::remove_const< typename C::value_type >::type | |
140 | >::type type; | |
141 | }; | |
142 | ||
143 | template< class C > | |
144 | struct extract_type | |
145 | : public extract_type_impl< C, is_list_container< C >::value > | |
146 | { | |
147 | }; | |
148 | ||
149 | ||
150 | /* | |
151 | ** | |
152 | */ | |
153 | ||
154 | template< | |
155 | typename ValueType, | |
156 | class Converter, | |
157 | bool wantList | |
158 | > | |
159 | class OuterSpace | |
160 | { | |
161 | public: | |
162 | typedef ValueType result_type; | |
163 | typedef typename boost::remove_reference< ValueType >::type unref_value_type; | |
164 | typedef typename boost::remove_const< unref_value_type>::type base_value_type; | |
165 | typedef typename boost::add_const< | |
166 | typename boost::add_reference< base_value_type >::type | |
167 | >::type const_ref_value_type; | |
168 | ||
169 | typedef FeedFunction FeedFunctionType; | |
170 | ||
171 | protected: | |
172 | friend class Var<ValueType, Converter>; | |
173 | ||
174 | OuterSpace(ValueType initial_value) | |
175 | : Value(initial_value) | |
176 | , InitialValue(initial_value) | |
177 | { | |
178 | } | |
179 | ||
180 | OuterSpace(const OuterSpace& other) | |
181 | : Value( other.Value) | |
182 | , InitialValue( other.InitialValue) | |
183 | { | |
184 | } | |
185 | ||
186 | bool audreyII(const std::string& v) | |
187 | { | |
188 | bool res= Converter()(v,Value); | |
189 | if (!res) Value= InitialValue; | |
190 | return res; | |
191 | } // eo audreyII(const std::string&) | |
192 | ||
193 | base_value_type Value; | |
194 | base_value_type InitialValue; | |
195 | }; // eo class OuterSpace | |
196 | ||
197 | ||
198 | template< | |
199 | typename ValueType, | |
200 | class Converter | |
201 | > | |
202 | class OuterSpace< ValueType, Converter, true > | |
203 | { | |
204 | public: | |
205 | typedef ValueType result_type; | |
206 | typedef typename boost::remove_reference< ValueType >::type unref_value_type; | |
207 | typedef typename boost::remove_const< unref_value_type>::type base_value_type; | |
208 | typedef typename boost::add_const< | |
209 | typename boost::add_reference< base_value_type >::type | |
210 | >::type const_ref_value_type; | |
211 | ||
212 | typedef FeedFunction2 FeedFunctionType; | |
213 | ||
214 | protected: | |
215 | friend class Var<ValueType, Converter>; | |
216 | ||
217 | OuterSpace(ValueType initial_value) | |
218 | : Value(initial_value) | |
219 | , InitialValue(initial_value) | |
220 | { | |
221 | } | |
222 | ||
223 | bool audreyII(const StringList& v) | |
224 | { | |
225 | Converter convert; | |
226 | Value.clear(); | |
227 | bool res= true; | |
228 | BOOST_FOREACH(const std::string& s, v) | |
229 | { | |
230 | typename base_value_type::value_type value; | |
231 | if (not convert(s,value)) | |
232 | { | |
233 | res = false; | |
234 | break; | |
235 | } | |
236 | Value.push_back( value ); | |
237 | } | |
238 | if (not res) Value= InitialValue; | |
239 | return res; | |
240 | } // eo audreyII(const std::string&) | |
241 | ||
242 | base_value_type Value; | |
243 | base_value_type InitialValue; | |
244 | }; // eo class OuterSpace | |
245 | ||
246 | } // eo namespace Detail | |
247 | ||
248 | ||
249 | /** | |
250 | * @brief default converter for the Var class. | |
251 | * @tparam ValueType the type of the result value (which the input should be converted to) | |
252 | * | |
253 | * Uses stringTo to convert the values. | |
254 | * | |
255 | * @see i2n::stringTo | |
256 | */ | |
257 | template< | |
258 | typename ValueType | |
259 | > | |
260 | struct DefaultConverter | |
261 | : public std::binary_function< std::string, ValueType&, bool > | |
262 | { | |
263 | ||
264 | bool operator () ( const std::string& str, ValueType& v ) | |
265 | { | |
266 | return string_to< ValueType >( str, v ); | |
267 | } // eo operator()(const std::string&,ValueType&) | |
268 | ||
269 | }; // eo struct DefaultConverter | |
270 | ||
271 | ||
272 | /** | |
273 | * @brief specialized converter for std::string result type. | |
274 | * | |
275 | * just copies the input to the result var and returns true. | |
276 | */ | |
277 | template<> struct DefaultConverter< std::string > | |
278 | : public std::binary_function< std::string, std::string&, bool > | |
279 | { | |
280 | ||
281 | bool operator () ( const std::string& str, std::string& v ) | |
282 | { | |
283 | v= str; | |
284 | return true; | |
285 | } // eo operator()(const std::string&,std::string&) | |
286 | ||
287 | }; // eo struct DefaultConverter< std::string > | |
288 | ||
289 | ||
290 | ||
291 | /** | |
292 | * @brief specialized converter for int types which accepts also octal and hex notation as input. | |
293 | * @tparam ValueType the type of the result value (which the input should be converted to) | |
294 | */ | |
295 | template< | |
296 | typename ValueType | |
297 | > | |
298 | struct AutoIntConverter | |
299 | : public std::binary_function< std::string, ValueType&, bool > | |
300 | { | |
301 | ||
302 | bool operator () ( const std::string& str, ValueType& v ) | |
303 | { | |
304 | std::istringstream istr(str); | |
305 | if (has_prefix(str,"0x")) | |
306 | { | |
307 | istr.get(); istr.get(); | |
308 | istr.setf( std::ios::hex, std::ios::basefield ); | |
309 | } | |
310 | else if (has_prefix(str,"0") or has_prefix(str,"-0")) | |
311 | { | |
312 | istr.setf( std::ios::oct, std::ios::basefield ); | |
313 | } | |
314 | istr >> v; | |
315 | return istr.eof(); | |
316 | } // eo operator()(const std::string&,ValueType&) | |
317 | ||
318 | }; // eo struct AutoIntConverter | |
319 | ||
320 | ||
321 | ||
322 | /** | |
323 | * @brief represents a configuration variable with automatic update. | |
324 | * @tparam ValueType type of the value this variable should hold. | |
325 | * @tparam Converter a converter class which converts a string (from config file) to the desired | |
326 | * type. | |
327 | * Needs to be derived from std::binary_function (taking a string as first arg, a reference to the | |
328 | * desired type as second and returning bool (@a true if conversion was succesful). | |
329 | * | |
330 | * Basic idea is to pass a point (section, key) in the config where the value for this | |
331 | * variable is stored. The config value (a string) should be converted to the desired value (type). | |
332 | * When the config is loaded (or reloaded) the var (content) is automatically updated. | |
333 | * | |
334 | * @see DefaultConverter | |
335 | * | |
336 | * @bug value accessing methods are @a const, but the result type may allow changing the internal value... | |
337 | */ | |
338 | template< | |
339 | typename ValueType, | |
340 | class Converter = DefaultConverter< | |
341 | typename Detail::extract_type< ValueType >::type | |
342 | > | |
343 | > | |
344 | class Var | |
345 | : public Detail::OuterSpace< | |
346 | ValueType, Converter, | |
347 | Detail::is_list_container< ValueType >::value > | |
348 | { | |
349 | BOOST_STATIC_ASSERT(( boost::is_base_of< | |
350 | std::binary_function< | |
351 | std::string, | |
352 | typename boost::add_reference< | |
353 | typename Detail::extract_type< ValueType >::type | |
354 | >::type, | |
355 | bool >, | |
356 | Converter >::value )); | |
357 | ||
358 | typedef Detail::OuterSpace< | |
359 | ValueType, Converter, | |
360 | Detail::is_list_container< ValueType >::value | |
361 | > MySpace; | |
362 | ||
363 | public: | |
364 | ||
365 | typedef typename MySpace::result_type result_type; | |
366 | typedef typename MySpace::base_value_type base_value_type; | |
367 | ||
368 | public: | |
369 | ||
370 | /** | |
371 | * @brief constructor with config location and initial value. | |
372 | * @param section the section in the config where the value is located in. | |
373 | * @param key the key (in the section) which identifies the value. | |
374 | * @param initial_value the initial value. | |
375 | */ | |
376 | Var(const std::string& section, | |
377 | const std::string& key, | |
378 | ValueType initial_value) | |
379 | : MySpace(initial_value) | |
380 | { | |
381 | FlowerShop= Detail::seymour( | |
382 | section, key, | |
383 | typename MySpace::FeedFunctionType( boost::bind( &MySpace::audreyII, this, _1 ) ) ); | |
384 | } | |
385 | ||
386 | ||
387 | /** | |
388 | * @brief constructor with config location (key in global section) and initial value. | |
389 | * @param key the key (in the global section) which identifies the value. | |
390 | * @param initial_value the initial value. | |
391 | */ | |
392 | Var(const std::string& key, | |
393 | ValueType initial_value) | |
394 | : MySpace(initial_value) | |
395 | { | |
396 | FlowerShop= Detail::seymour( | |
397 | "", key, | |
398 | typename MySpace::FeedFunctionType( boost::bind( &MySpace::audreyII, this, _1 ) ) ); | |
399 | } | |
400 | ||
401 | ||
402 | /** | |
403 | * @brief operator for accessing the value. | |
404 | * @return the current value. | |
405 | */ | |
406 | result_type operator()() const { return MySpace::Value; } | |
407 | ||
408 | ||
409 | /** | |
410 | * @brief method for accessing the value. | |
411 | * @return the current value. | |
412 | */ | |
413 | result_type get_value() const { return MySpace::Value; } | |
414 | ||
415 | ||
416 | /** | |
417 | * @brief copy constructor. | |
418 | * @param other the instance to copy from. | |
419 | */ | |
420 | Var(const Var& other) | |
421 | : MySpace( other ) | |
422 | { | |
423 | FlowerShop= Detail::seymour( | |
424 | other.FlowerShop, | |
425 | typename MySpace::FeedFunctionType( boost::bind( &MySpace::audreyII, this, _1 ) ) ); | |
426 | } // eo Var(const Var&) | |
427 | ||
428 | ||
429 | /** | |
430 | * @brief cast operator for the underlying value type. delivers the value. | |
431 | * @return the current value. | |
432 | * | |
433 | * The cast operator allows the instance to be used whereever the underlying value type | |
434 | * is expected. | |
435 | */ | |
436 | operator result_type () const { return MySpace::Value; } | |
437 | ||
438 | ||
439 | private: | |
440 | ||
441 | Detail::FlowerShop FlowerShop; | |
442 | ||
443 | }; // eo class Var | |
444 | ||
445 | ||
446 | ||
447 | /** | |
448 | * @brief convenience: \<\< operator which streams the current value of a config var. | |
449 | */ | |
450 | template < | |
451 | typename ValueType, | |
452 | class Converter | |
453 | > | |
454 | std::ostream& operator<<(std::ostream& o, const Var< ValueType, Converter >& v) | |
455 | { | |
456 | return o << v(); | |
457 | } // eo operator<<(std::ostream&,const Var< ValueType, Converter>& | |
458 | ||
459 | ||
460 | bool set_config_file(const std::string& path); | |
461 | ||
462 | std::string get_config_file(); | |
463 | ||
464 | bool reload(); | |
465 | ||
466 | ||
467 | } // eo namespace Config | |
468 | ||
469 | } // eo namespace I2n | |
470 | ||
471 | #endif |