root/library/bdm/base/user_info.h @ 471

Revision 471, 21.4 kB (checked in by mido, 15 years ago)

1) ad UserInfo?: UI::get a UI::build predelany tak, ze vraci fals / NULL v pripade neexistence pozadovaneho Settingu, pridana enumericky typ UI::SettingPresence?, predelany stavajici UI implementace, dodelana UI dokumentace
2) dokoncena konfigurace ASTYLERU, brzy bude aplikovan
3) doxygen nastaven tak, ze vytvari soubor doxy_warnings.txt

  • Property svn:eol-style set to native
Line 
1/*!
2  \file
3  \brief UI (user info) class for loading/saving objects from/to configuration files.
4  It is designed with use of libconfig C/C++ Configuration File Library
5  \ref ui_page
6  \author Vaclav Smidl.
7
8  -----------------------------------
9  BDM++ - C++ library for Bayesian Decision Making under Uncertainty
10
11  Using IT++ for numerical operations
12  -----------------------------------
13*/
14
15#ifndef USER_INFO_H
16#define USER_INFO_H
17
18#include <stdio.h>
19#include <string>
20#include <typeinfo>
21#include <map>
22#include <stdexcept>
23
24#include "libconfig/libconfig.h++"
25#include "../bdmroot.h"
26#include "itpp/itbase.h"
27
28
29using std::string;
30using namespace std;
31using namespace libconfig;
32
33namespace bdm {
34
35//! Exception prepared for reporting user-info errors which are always related to some concrete Setting path
36//!
37//!  \ref ui_page
38class UIException : public std::exception {
39
40public:
41        //! Error message
42        const string message;
43
44        //! Path to the problematic setting
45        const string path;
46
47        //! Use this constructor when you can pass the problematical Setting as a parameter
48        UIException ( const string &message, const Setting &element )
49                        : message ( "UI error: " + message + "." ), path ( "Check path \"" + string ( element.getPath() ) + "\"." ) {
50        }
51
52        //! This constructor is for other occasions, when only path of problematical Setting is known
53        UIException ( const string &message, const string &path )
54                        : message ( "UI error: " + message + "." ), path ( "Check path \"" + path + "\"." ) {
55        }
56
57        //! Overriden method for reporting an error message
58        virtual const char* what() const throw() {
59                return ( message + " " + path ).c_str();
60        }
61        ~UIException() throw() {};
62};
63
64
65/*!
66@brief This class serves to load and/or save user-infos into/from
67configuration files stored on a hard-disk.
68
69Firstly, save some user-infos into the new UIFile instance. Then,
70call the save method with a filename as its only argument:
71
72\code
73        CAudi audi;
74        UIFile file;
75        UI::save( audi, file, "TT");
76        file.save("cars.cfg");
77\endcode
78
79In the other way round, when loading object from a configuration file,
80the appropriate code looks like this:
81
82\code
83        UIFile file("cars.cfg");
84        CAudi *audi = UI::build<CAudi>(file,"TT");
85\endcode
86
87\ref ui_page
88*/
89class UIFile : public Config {
90public:
91        //! Create empty file instance prepared to store Settings
92        UIFile();
93
94        //! Creates instance and fills it from the configuration file file_name
95        UIFile ( const string &file_name );
96
97        //! Save all the stored Settings into the configuration file file_name
98        void save ( const string &file_name );
99
100        //! This operator allows the ability of substituting Setting parameter by UIFile instance
101        operator Setting&();
102};
103
104/*!
105@brief This class serves to expand links used within configuration files.
106
107Value of any type but string can be linked to some other value of the same type
108defined elsewhere in the current configuration file or even in some different
109configuration file.
110
111Link have three parts, \<name\> : \<path\> \<\@filename\>. Field \<name\> contains the
112name of the new setting, \<path\> is the relative path to the referenced setting, which
113has to be taken from the %root Setting element. The last part \<\@filename\> is optional,
114it contains filename in the case the link should refer to a variable stored in a different
115file. From the previous part \<path\>, it has to be separated by '@'.
116
117\code
118    ...
119        jardovo :
120        {
121          class = "Car";
122          year = 1992;
123          manufacturer = "liaz";
124          kilometers = 1555000;
125        };
126        ondrejovo :
127        {
128          class = "Bike";
129          year = 1996;
130          manufacturer = "author";
131          electricLights = true;
132          matr = ( 2, 2, [ 1.0, 0.0, 0.0, 1.0 ] );
133        };
134
135        #this is the example of local link to another mean of transport
136        elisky = "jardovo";
137
138        ...
139
140        # And this link is external link pointing to the file "other_cars.cfg" stored in the
141        # same directory. In that file, it refers to the local Setting "magic_cars.skubankovo".
142        kati = "magic_cars.skubankovo@other_cars.cfg";
143
144    ...
145\endcode
146
147When you want to expand a possible linked setting "element" within your code, it has to be treated this way:
148
149\code
150        ...
151
152        const SettingResolver link( element );
153
154        ...
155
156        int len = link.result.getLength();
157
158        ...
159\endcode
160
161The whole point is that a resolved link (class member #result, i.e., "link.result" in the previous example) could point
162into a different configuration file. In that case there has to be an UIFile instance managing reading from this
163file. As the libconfig::Config deletes all its Settings when dealocated, UIFile must not be dealocated until all
164the necessary operation on the linked Setting are finished (otherwise, the link #result would be invalid just after
165the UIFile dealocation). And that is exactly the mechanism implemented within SettingResolver class. It assures,
166that the #result Setting reference is valid within the scope of SettingResolver instance.
167       
168\ref ui_page
169 */
170class SettingResolver : root {
171private:
172        //! If necessary, this pointer stores an addres of an opened UIFile, else it equals NULL
173        UIFile *file;
174
175        //! This method initialize #result reference, i.e., it executes the main code of SettingResolver class
176        //!
177        //! This code could be also located directly in constructor. The only reason why we made this
178        //! method is the keyword 'const' within the declaration of #result reference . Such a reference
179        //! have to be intialized before any other constructor command, exactly in the way it is implemented now.
180        const Setting &initialize_reference ( UIFile* &file, const Setting &potential_link );
181
182public:
183        //! Reference to a resolved link or to the original Setting in the case it does not contain a link
184        const Setting &result;
185
186        //! If potential_link contains a link to some other setting, it is resolved here. Anyway, the Setting reference #result is prepared for use.
187        SettingResolver ( const Setting &potential_link );
188
189        //! An opened UIFile file is closed here if necessary.
190        ~SettingResolver();
191};
192
193/*!
194@brief UI is an abstract class which collects all the auxiliary functions useful to prepare some concrete
195user-infos.
196
197See static methods 'build', 'get' and 'save'. Writing user-infos with these methods is rather  simple. The
198rest of this class is intended for internal purposes only. Its meaning is to allow pointers to its templated
199descendant ParticularUI<T>.
200
201\ref ui_page
202*/
203class UI {
204private:
205        //! Class with state shared across all its instances ("monostate"), encapsulating two maps, one mapping names to UI instances and the other mapping type_infos to class names
206        //!
207        //! The key property of this class is that it initializes the internal maps on global init,
208        //! before the instance is used for a first time. Therefore, we do not have to care about initialization
209        //! during a call of UIREGISTER macro operating with both these mappings.
210        class MappedUI {
211        private:
212                //! Type definition of mapping which transforms class names to the related UI instances
213                typedef map< const string, const UI* const > StringToUIMap;
214
215                //! Type definition of mapping which transforms RTTI type_infos to the related class names
216                typedef map< const type_info * const, const string > TypeInfoToStringMap;
217
218                //! Immediately initialized instance of type StringToUIMap
219                static StringToUIMap& mapped_strings();
220
221                //! Immediately initialized instance of type TypeInfoToStringMap
222                static TypeInfoToStringMap& mapped_type_infos();
223
224                //! Method for reporting a error when an attempt to operate with an unregistered class occures
225                static void unregistered_class_error ( const string &unregistered_class_name );
226
227        public:
228                //! Add a pair key-userinfo into the internal map
229                static void add_class ( const string &class_name, const type_info * const class_type_info, const UI* const ui );
230
231                //! Search for an userinfo related to the passed class name within the internal map
232                static const UI& retrieve_ui ( const string &class_name );
233
234                //! Search for an class name related to the passed type_info within the internal map
235                static const string& retrieve_class_name ( const type_info* const class_type_info );
236        };
237
238        //! Function assertting that the setting element is of the SettingType type
239        static void assert_type ( const Setting &element, Setting::Type type );
240
241        //! Method assembling a typeless instance, it is implemented in descendant class ParticularUI<T>
242        virtual root* new_instance() const = 0;
243
244        //! Method switching from the \a element to its child Setting according the passed \a index, it also does all the necessary error-checking
245        static const Setting& to_child_setting ( const Setting &element, const int index );
246
247        //! Method switching from the \a element to its child Setting according the passed \a name, it also does all the necessary error-checking
248        static const Setting& to_child_setting ( const Setting &element, const string &name );
249
250        //! This method converts a Setting into a matrix
251        static void from_setting ( mat& matrix, const Setting &element );
252        //! This method converts a Setting into an integer vector
253        static void from_setting ( ivec &vector, const Setting &element );
254        //! This method converts a Setting into a string
255        static void from_setting ( string &str, const Setting &element );
256        //! This method converts a Setting into a real vector
257        static void from_setting ( vec &vector, const Setting &element );
258        //! This method converts a Setting into a integer scalar
259        static void from_setting ( int &integer, const Setting &element );
260        //! This method converts a Setting into a real scalar
261        static void from_setting ( double &real, const Setting &element );
262        //! This method converts a Setting into a class T descendant
263        template<class T> static void from_setting ( T* &instance, const Setting &element ) {
264                const SettingResolver link ( element );
265                assert_type ( link.result, Setting::TypeGroup );
266
267                // we get a value stored in the "class" attribute
268                string class_name;
269                if ( !link.result.lookupValue ( "class", class_name ) )
270                        throw UIException ( "the obligatory \"class\" identifier is missing", link.result );
271
272                // then we find a user-info related to this type
273                const UI& related_UI = MappedUI::retrieve_ui ( class_name );
274
275                root* typeless_instance = related_UI.new_instance();
276
277                instance = dynamic_cast<T*> ( typeless_instance );
278                if ( !instance )
279                        throw UIException ( "class " + class_name + " is not a descendant of the desired output class. Try to call the UI::build<T> function with a different type parameter.", link.result );
280
281                try {
282                        instance->from_setting ( link.result );
283                } catch ( SettingException sttng_xcptn ) {
284                        string msg = "the method " + class_name + ".from_setting(Setting&) has thrown a SettingException. Try to correct this method. Check path \"" + sttng_xcptn.getPath() + "\".";
285                        throw exception ( msg.c_str() );
286                } catch ( UIException uixcptn ) {
287                        string msg = "the method " + class_name + ".from_setting(Setting&) has thrown an UIException \"" + uixcptn.message + "\" Try to correct this method. Check path \"" + uixcptn.path + "\".";
288                        throw exception ( msg.c_str() );
289                } catch ( exception xcptn ) {
290                        string msg = "the method " + class_name + ".from_setting(Setting&) has thrown a general exception \"" + xcptn.what() + "\" Try to correct this method. Check path \"" + element.getPath() + "\".";
291                        throw exception ( msg.c_str() );
292                }
293        }
294
295        //! This methods converts a Setting into a new templated array of type Array<T>
296        template<class T> static void from_setting ( Array<T> &array_to_load, const Setting &element ) {
297                const SettingResolver link ( element );
298
299                assert_type ( link.result, Setting::TypeList );
300
301                int len = link.result.getLength();
302                array_to_load.set_length ( len );
303                if ( len == 0 ) return;
304
305                for ( int i = 0; i < len; i++ )
306                        from_setting ( array_to_load ( i ), link.result[i] );
307        }
308
309        //! This is dummy version of the from_setting method for other, unsupported types. It just throws an exception.
310        //!
311        //! At the moment, this is the only way how to compile the library without obtaining the compiler error c2665.
312        //! The exception can help to find the place where the template is misused and also to correct it.
313        template<class T> static void from_setting ( T &array_to_load, const Setting &element ) {
314                throw UIException( "from_setting is not implemented for this type", element );
315        }
316
317
318protected:
319        //! Default constructor for internal use only, see \sa ParticularUI<T>
320        UI ( const string& class_name, const type_info * const class_type_info ) {
321                MappedUI::add_class ( class_name, class_type_info, this );
322        }
323
324public:
325
326        //! Enumerical type used to determine whether the data for concrete Settingis is compulsory or optional
327        enum SettingPresence { optional, compulsory } ;
328
329        //! \name Initialization of classes
330        //!@{
331        //! The type T has to be a #bdm::root descendant class
332
333        //! The new instance of type T* is constructed and initialized with values stored in the Setting element[name]
334        //!
335        //! If there is not any sub-element named #name, the null pointer is returned.
336        template<class T> static T* build ( const Setting &element, const string &name, SettingPresence settingPresence = optional ) {
337                if ( !element.exists ( name ) )
338                {
339                        if( settingPresence == optional )
340                                return NULL;
341                        else
342                                throw UIException ( "the compulsory Setting named \"" + name + "\" is missing", element );
343                }
344
345                T* instance;
346                from_setting<T> ( instance, to_child_setting ( element, name ) );
347                return instance;
348        }
349
350        //! The new instance of type T* is constructed and initialized with values stored in the Setting element[index]
351        //!
352        //! If there is not any sub-element indexed by #index, the null pointer is returned.
353        template<class T> static T* build ( const Setting &element, const int index, SettingPresence settingPresence = optional ) {
354                if ( element.getLength() <= index )
355                {
356                        if( settingPresence == optional )
357                                return NULL;
358                        else
359                        {
360                                stringstream stream;
361                                stream << index;
362                                throw UIException ( "the compulsory Setting with the index " + stream.str()+ " is missing", element );
363                        }
364                }
365
366                T* instance;
367                from_setting<T> ( instance, to_child_setting ( element, index ) );
368                return instance;
369        }
370
371        //! The new instance of type T* is constructed and initialized with values stored in the Setting element
372        template<class T> static T* build ( const Setting &element ) {
373                T* instance;
374                from_setting<T> ( instance,  element );
375                return instance;
376
377        }
378
379        //!@}
380
381        //! \name Initialization of structures
382        //!@{
383        //! The type T has to be int, double, string, vec, ivec or mat.
384
385        //! The existing instance of type T is initialized with values stored in the Setting element[name]
386        //! If there is not any sub-element named #name, this method returns false.
387        template<class T> static bool get ( T &instance, const Setting &element, const string &name, SettingPresence settingPresence = optional ) {
388                if ( !element.exists ( name ) )
389                {
390                        if( settingPresence == optional )
391                                return false;
392                        else
393                                throw UIException ( "the compulsory Setting named \"" + name + "\" is missing", element );
394                }
395
396                from_setting ( instance, to_child_setting ( element, name ) );
397                return true;
398        }
399
400        //! The existing instance of type T is initialized with values stored in the Setting element[index]
401        //! If there is not any sub-element indexed by #index, this method returns false.
402        template<class T> static bool get ( T &instance, const Setting &element, const int index, SettingPresence settingPresence = optional ) {
403                if ( element.getLength() <= index )
404                {
405                        if( settingPresence == optional )
406                                return false;
407                        else
408                        {
409                                stringstream stream;
410                                stream << index;
411                                throw UIException ( "the compulsory Setting with the index " + stream.str()+ " is missing", element );
412                        }
413                }
414
415                from_setting ( instance, to_child_setting ( element, index ) );
416                return true;
417        }
418
419        //! The existing instance of type T is initialized with values stored in the Setting element directly
420        template<class T> static bool get ( T &instance, const Setting &element ) {
421                from_setting ( instance, element );
422                return true;
423        }
424        //!@}
425
426        //! \name Initialization of arrays Array<T>
427        //!@{
428        //! The type T has to be int, double, string, vec, ivec or mat, or pointer to any root descendant.
429
430        //! The existing array of type T is initialized with values stored in the Setting element[name]
431        //! If there is not any sub-element named #name, this method returns false.
432        template<class T> static bool get ( Array<T> &array_to_load, const Setting &element, const string &name, SettingPresence settingPresence = optional ) {
433                if ( !element.exists ( name ) )
434                        return false;
435
436                from_setting ( array_to_load, to_child_setting ( element, name ) );
437                return true;
438        }
439
440        //! The existing array of type T is initialized with values stored in the Setting element[index]
441        //! If there is not any sub-element indexed by #index, this method returns false.
442        template<class T> static bool get ( Array<T> &array_to_load, const Setting &element, const int index, SettingPresence settingPresence = optional ) {
443                if ( element.getLength() <= index )
444                        return false;
445
446                from_setting ( array_to_load, to_child_setting ( element, index ) );
447                return true;
448        }
449
450        //! The existing array of type T is initialized with values stored in the Setting element
451        template<class T> static bool get ( Array<T> &array_to_load, const Setting &element ) {
452                from_setting ( array_to_load, element );
453                return true;
454        }
455        //!@}
456
457        //! \name Serialization of objects and structures into a new Setting
458        //!@{
459        //! The new child Setting can be accessed either by its name - if some name is passed as a parameter -
460        //! or by its integer index. In that case, the new element is added at the very end of the current list of child Settings.
461
462        //! A root descendant instance is stored in the new child Setting appended to the passed element
463        template< class T> static void save ( const T * const instance, Setting &element, const string &name = "" ) {
464                Setting &set = ( name == "" ) ? element.add ( Setting::TypeGroup )
465                               : element.add ( name, Setting::TypeGroup );
466
467                const string &class_name = MappedUI::retrieve_class_name ( &typeid ( *instance ) );
468
469                // add attribute "class"
470                Setting &type = set.add ( "class", Setting::TypeString );
471                type = class_name;
472
473                try {
474                        instance->to_setting ( set );
475                } catch ( SettingException sttng_xcptn ) {
476                        string msg = "the method " + class_name + ".to_setting(Setting&) has thrown a SettingException. Try to correct this method. Check path \"" + sttng_xcptn.getPath() + "\".";
477                        throw exception ( msg.c_str() );
478                } catch ( UIException uixcptn ) {
479                        string msg = "the method " + class_name + ".to_setting(Setting&) has thrown an UIException \"" + uixcptn.message + "\" Try to correct this method. Check path \"" + uixcptn.path + "\".";
480                        throw exception ( msg.c_str() );
481                } catch ( exception xcptn ) {
482                        string msg = "the method " + class_name + ".to_setting(Setting&) has thrown a general exception \"" + xcptn.what() + "\" Try to correct this method. Check path \"" + element.getPath() + "\".";
483                        throw exception ( msg.c_str() );
484                }
485        }
486
487        //! An Array<T> instance is stored in the new child Setting appended to the passed element
488        template<class T> static void save ( const Array<T> &array_to_save, Setting &element, const string &name = "" ) {
489                assert_type ( element, Setting::TypeGroup );
490                Setting &list = ( name == "" ) ? element.add ( Setting::TypeList )
491                                : element.add ( name, Setting::TypeList );
492                for ( int i = 0; i < array_to_save.length(); i++ )
493                        save ( array_to_save ( i ), list );
494        }
495
496        //! A matrix(of type mat) is stored in the new child Setting appended to the passed element
497        static void save ( const mat &matrix, Setting &element, const string &name = "" );
498
499        //! An integer vector (of type ivec) is stored in the new child Setting appended to the passed element
500        static void save ( const ivec &vec, Setting &element, const string &name = "" );
501
502        //! A double vector (of type vec) is stored in the new child Setting appended to the passed element
503        static void save ( const vec &vector, Setting &element, const string &name = "" );
504
505        //! A string is stored in the new child Setting appended to the passed element
506        static void save ( const string &str, Setting &element, const string &name = "" );
507
508        //! An integer is stored in the new child Setting appended to the passed element
509        static void save ( const int &integer, Setting &element, const string &name = "" );
510
511        //! A double is stored in the new child Setting appended to the passed element
512        static void save ( const double &real, Setting &element, const string &name = "" );
513        //!@}
514
515};
516
517
518//! The only UI descendant class which is not intended for direct use. It should be accessed within the ::UIREGISTER macro only.
519//! \ref ui_page
520template<typename T> class ParticularUI : private UI {
521private:
522        //! Default constructor, which is intentionally declared as private
523        ParticularUI<T> ( const string &class_name ) : UI ( class_name, &typeid ( T ) ) {};
524
525public:
526        //! The only instance of this class (each type T has its own instance) which is used as a factory for processing related UI
527        static const ParticularUI<T>& factory;
528
529        //! A method returning a brand new instance of class T, this method is the reason why there have to be a parameterless construcotor in class T
530        root* new_instance() const {
531                return new T();
532        }
533};
534
535}
536
537/*!
538  \def UIREGISTER(class_name)
539  \brief Macro for registration of class into map of user-infos, registered class is scriptable using UI static methods
540
541  Argument \a class_name has to be a descendant of root class and also, it has to have parameterless constructor prepared.
542  This macro should be used in header file, immediately after a class declaration.
543
544  \ref ui_page
545*/
546#ifndef BDMLIB
547#define UIREGISTER(class_name) template<> const ParticularUI<class_name>& ParticularUI<class_name>::factory = ParticularUI<class_name>(#class_name)
548#else
549#define UIREGISTER(class_name)
550#endif
551
552#endif // #ifndef USER_INFO_H
Note: See TracBrowser for help on using the browser.