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

Revision 1063, 24.3 kB (checked in by mido, 14 years ago)

a small patch of documentation, to be contiuned..

  • 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
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 "../bdmroot.h"
25#include "../shared_ptr.h"
26#include "itpp/itbase.h"
27#include "../math/square_mat.h"
28#include "../math/chmat.h"
29
30using std::string;
31using namespace std;
32using namespace libconfig;
33
34namespace bdm {
35
36//! Generic exception for reporting configuration errors
37//!
38//!  \ref ui
39class UIException : public std::exception {
40private:
41        //! Error message
42        const string message;
43
44public:
45        /*!
46          \brief The constructor
47          \param message the reason for throwing the exception. Should be a complete English sentence (or a couple sentences), starting with "UIException: ".
48        */
49        UIException ( const string &message ) :
50                        message ( message ) {
51        }
52
53        //! Overriden method for reporting the error message
54        virtual const char* what() const throw() {
55                return message.c_str();
56        }
57
58        ~UIException() throw() {};
59
60protected:
61        /*!
62          Formats error messages for derived classes, which use a
63          Setting path in addition to the message.
64        */
65        static string format_message ( const string &reason, const string &path );
66};
67
68//! Exception for reporting configuration errors related to some concrete Setting path
69//!
70//!  \ref ui
71class UISettingException : public UIException {
72public:
73        //! Use this constructor when you can pass the problematical Setting as a parameter
74        UISettingException ( const string &message, const Setting &element ) :
75                        UIException ( format_message ( message, string ( element.getPath() ) ) ) {
76        }
77
78        //! This constructor is for other occasions, when only path of problematical Setting is known
79        UISettingException ( const string &message, const string &path ) :
80                        UIException ( format_message ( message, path ) ) {
81        }
82
83        ~UISettingException() throw() {};
84};
85
86//! Exception for reporting configuration errors in the "class" attribute
87//!
88//!  \ref ui
89class UIClassException : public UIException {
90public:
91        //! Use this constructor when you can pass the problematical Setting as a parameter
92        UIClassException ( const string &message, const Setting &element ) :
93                        UIException ( format_message ( message, string ( element.getPath() ) ) ) {
94        }
95
96        //! This constructor is for other occasions, when only path of problematical Setting is known
97        UIClassException ( const string &message, const string &path ) :
98                        UIException ( format_message ( message, path ) ) {
99        }
100
101        ~UIClassException() throw() {};
102};
103
104/*!
105@brief This class serves to load and/or save user-infos into/from
106configuration files stored on a hard-disk.
107
108Firstly, save some user-infos into the new UIFile instance. Then,
109call the save method with a filename as its only argument:
110
111\code
112        CAudi audi;
113        UIFile file;
114        UI::save( audi, file, "TT");
115        file.save("cars.cfg");
116\endcode
117
118In the other way round, when loading object from a configuration file,
119the appropriate code looks like this:
120
121\code
122        UIFile file("cars.cfg");
123        CAudi *audi = UI::build<CAudi>(file,"TT");
124\endcode
125
126\ref ui
127*/
128class UIFile : public Config {
129public:
130        //! Create empty file instance prepared to store Settings
131        UIFile();
132
133        //! Creates instance and fills it from the configuration file file_name
134        UIFile ( const string &file_name );
135
136        //! Save all the stored Settings into the configuration file file_name
137        void save ( const string &file_name );
138
139        //! This operator allows the ability of substituting Setting parameter by UIFile instance
140        operator Setting&();
141};
142
143/*!
144@brief This class serves to expand links used within configuration files.
145
146Value of any type but string can be linked to some other value of the same type
147defined elsewhere in the current configuration file or even in some different
148configuration file.
149
150Link have three parts, \<name\> : \<path\> \<\@filename\>. Field \<name\> contains the
151name of the new setting, \<path\> is the relative path to the referenced setting, which
152has to be taken from the %root Setting element. The last part \<\@filename\> is optional,
153it contains filename in the case the link should refer to a variable stored in a different
154file. From the previous part \<path\>, it has to be separated by '@'.
155
156\code
157    ...
158        jardovo :
159        {
160          class = "Car";
161          year = 1992;
162          manufacturer = "liaz";
163          kilometers = 1555000;
164        };
165        ondrejovo :
166        {
167          class = "Bike";
168          year = 1996;
169          manufacturer = "author";
170          electricLights = true;
171          matr = ( 2, 2, [ 1.0, 0.0, 0.0, 1.0 ] );
172        };
173
174        #this is the example of local link to another mean of transport
175        elisky = "jardovo";
176
177        ...
178
179        # And this link is external link pointing to the file "other_cars.cfg" stored in the
180        # same directory. In that file, it refers to the local Setting "magic_cars.skubankovo".
181        kati = "magic_cars.skubankovo@other_cars.cfg";
182
183    ...
184\endcode
185
186When you want to expand a possible linked setting "element" within your code, it has to be treated this way:
187
188\code
189        ...
190
191        const SettingResolver link( element );
192
193        ...
194
195        int len = link.result.getLength();
196
197        ...
198\endcode
199
200The whole point is that a resolved link (class member #result, i.e., "link.result" in the previous example) could point
201into a different configuration file. In that case there has to be an UIFile instance managing reading from this
202file. As the libconfig::Config deletes all its Settings when dealocated, UIFile must not be dealocated until all
203the necessary operation on the linked Setting are finished (otherwise, the link #result would be invalid just after
204the UIFile dealocation). And that is exactly the mechanism implemented within SettingResolver class. It assures,
205that the #result Setting reference is valid within the scope of SettingResolver instance.
206
207\ref ui
208 */
209class SettingResolver : root {
210private:
211        //! If necessary, this pointer stores an addres of an opened UIFile, else it equals NULL
212        UIFile *file;
213
214        //! This method initialize #result reference, i.e., it executes the main code of SettingResolver class
215        //!
216        //! This code could be also located directly in constructor. The only reason why we made this
217        //! method is the keyword 'const' within the declaration of #result reference . Such a reference
218        //! have to be intialized before any other constructor command, exactly in the way it is implemented now.
219        const Setting &initialize_reference ( UIFile* &file, const Setting &potential_link );
220
221public:
222        //! Reference to a resolved link or to the original Setting in the case it does not contain a link
223        const Setting &result;
224
225        //! If potential_link contains a link to some other setting, it is resolved here. Anyway, the Setting reference #result is prepared for use.
226        SettingResolver ( const Setting &potential_link );
227
228        //! An opened UIFile file is closed here if necessary.
229        ~SettingResolver();
230};
231
232
233
234/*!
235@brief UI is an abstract class which collects all the auxiliary functions useful to prepare some concrete
236user-infos.
237
238See static methods 'build', 'get' and 'save'. Writing user-infos with these methods is rather simple. The
239rest of this class is intended for internal purposes only. Its meaning is to allow pointers to its templated
240descendant ParticularUI<T>.
241
242\ref ui
243*/
244class UI {
245private:
246        //! 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
247        //!
248        //! The key property of this class is that it initializes the internal maps on global init,
249        //! before the instance is used for a first time. Therefore, we do not have to care about initialization
250        //! during a call of UIREGISTER macro operating with both these mappings.
251        class MappedUI {
252        private:
253                //! Type definition of mapping which transforms class names to the related UI instances
254                typedef map< const string, const UI* const > StringToUIMap;
255
256                //! Type definition of mapping which transforms RTTI type_infos to the related class names
257                typedef map< const type_info * const, const string > TypeInfoToStringMap;
258
259                //! Immediately initialized instance of type StringToUIMap
260                static StringToUIMap& mapped_strings();
261
262                //! Immediately initialized instance of type TypeInfoToStringMap
263                static TypeInfoToStringMap& mapped_type_infos();
264
265                //! Method for reporting a error when an attempt to operate with an unregistered class occures
266                static void unregistered_class_error ( const string &unregistered_class_name );
267
268        public:
269                //! Add a pair key-userinfo into the internal map
270                static void add_class ( const string &class_name, const type_info * const class_type_info, const UI* const ui );
271
272                //! Search for an userinfo related to the passed class name within the internal map
273                static const UI& retrieve_ui ( const string &class_name );
274
275                //! Search for an class name related to the passed type_info within the internal map
276                static const string& retrieve_class_name ( const type_info* const class_type_info );
277        };
278
279        //! Function assertting that the setting element is of the SettingType type
280        static void assert_type ( const Setting &element, Setting::Type type );
281
282        /*!
283          \brief Method constructing a configured instance
284
285          The returned pointer must be allocated using operator new
286          (it's deleted at the end of its life cycle). The method is
287          implemented in descendant class ParticularUI<T>, which knows
288          the correct type T.
289        */
290        virtual root* new_instance() const = 0;
291
292        //! Method switching from the \a element to its child Setting according the passed \a index, it also does all the necessary error-checking
293        static const Setting& to_child_setting ( const Setting &element, const int index );
294
295        //! Method switching from the \a element to its child Setting according the passed \a name, it also does all the necessary error-checking
296        static const Setting& to_child_setting ( const Setting &element, const string &name );
297
298        //! A shortcut for calling instance.from_setting( set ); with some error catches added
299        static void call_from_setting( root &instance, const Setting &set, string class_name = "unknown");
300
301        //! A shortcut for calling instance.to_setting( set ); with some error catches added
302        static void call_to_setting( const root &instance, Setting &set, string class_name = "unknown" );
303
304        //! This method converts a Setting into a matrix
305        static void from_setting ( mat& matrix, const Setting &element );
306        //! This method converts a Setting into a ldmat
307        static void from_setting ( ldmat& matrix, const Setting &element );
308        //! This method converts a Setting into an integer vector
309        static void from_setting ( ivec &vector, const Setting &element );
310        //! This method converts a Setting into a string
311        static void from_setting ( string &str, const Setting &element );
312        //! This method converts a Setting into a real vector
313        static void from_setting ( vec &vector, const Setting &element );
314        //! This method converts a Setting into a integer scalar
315        static void from_setting ( int &integer, const Setting &element );
316        //! This method converts a Setting into a real scalar   
317        static void from_setting ( double &real, const Setting &element );
318        //! This method converts a Setting into a staticallly allocated root descendant
319        static void from_setting ( root &instance, const Setting &element );
320        //! This method converts a Setting into a log_level
321        //!
322        //! Data is stored in string which has different meaning than usally. For that reason, a specialized
323        //! method is necessary here to avoid the default call of "const SettingResolver link ( element );",
324        //! which would lead to erroneous behaviour in this case
325        static void from_setting ( log_level_base &log_level, const Setting &element );
326        //! This method converts a Setting into a dynamically allocated root descendant
327        template<class T> static void from_setting ( T* &instance, const Setting &element ) {
328                const SettingResolver link ( element );
329                assert_type( link.result, Setting::TypeGroup );
330
331                // we get a value stored in the "class" attribute
332                string class_name;
333                if ( !link.result.lookupValue ( "class", class_name ) )
334                        throw UIClassException ( "UIException: the obligatory \"class\" identifier is missing.", link.result );
335
336                // then we find a user-info related to this type
337                const UI& related_UI = MappedUI::retrieve_ui ( class_name );
338
339                root *typeless_instance = related_UI.new_instance();
340                bdm_assert ( typeless_instance, "UI::new_instance failed" );
341
342                instance = dynamic_cast<T*> ( typeless_instance );
343                if ( !instance ) {
344                        delete typeless_instance;
345                        throw UIClassException ( "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 );
346                }
347
348                try {
349                        call_from_setting( *instance, link.result, class_name );
350                } catch ( ... ) {
351                        delete instance;
352                        instance = 0;
353                        throw;
354                }
355        }
356
357        //! This method converts a Setting into a descendant of class
358        //! T, wrapped in an instance of bdm::shared_ptr<T> .
359        template<class T> static void from_setting ( bdm::shared_ptr<T> &instance, const Setting &element ) {
360                T *tmp_inst = 0;
361                from_setting ( tmp_inst, element );
362                bdm_assert ( tmp_inst, "UI::from_setting failed" );
363                instance = tmp_inst;
364        }
365
366        //! This methods converts a Setting into a new templated array of type Array<T>
367        template<class T> static void from_setting ( Array<T> &array_to_load, const Setting &element ) {
368                const SettingResolver link ( element );
369
370                assert_type ( link.result, Setting::TypeList );
371
372                int len = link.result.getLength();
373                array_to_load.set_length ( len );
374                if ( len == 0 ) return;
375
376                for ( int i = 0; i < len; i++ )
377                        from_setting ( array_to_load ( i ), link.result[i] );
378        }
379
380protected:
381        //! Constructor for internal use only, see \sa ParticularUI<T>
382        UI ( const string& class_name, const type_info * const class_type_info ) {
383                MappedUI::add_class ( class_name, class_type_info, this );
384        }
385
386public:
387
388        //! Enum type used to determine whether the data for concrete Settingis is compulsory or optional
389        enum SettingPresence { optional, compulsory } ;
390
391        //! \name Initialization of classes
392        //!@{
393        //! The type T has to be a #bdm::root descendant class
394
395        //! The new instance of type T* is constructed and initialized with values stored in the Setting element[name]
396        //!
397        //! If there does not exist any sub-element named name and settingPresence is optional, an empty bdm::shared_ptr<T> is returned.
398        //! When settingPresence is compulsory, the returned bdm::shared_ptr<T> is never empty (an exception is thrown when the object isn't found).
399        template<class T>
400        static bdm::shared_ptr<T> build ( const Setting &element, const string &name, SettingPresence settingPresence = optional ) {
401                if ( !element.exists ( name ) ) {
402                        if ( settingPresence == optional )
403                                return bdm::shared_ptr<T>();
404                        else
405                                throw UISettingException ( "UIException: the compulsory Setting named \"" + name + "\" is missing.", element );
406                }
407
408                bdm::shared_ptr<T> instance;
409                from_setting<T> ( instance, to_child_setting ( element, name ) );
410                return instance;
411        }
412
413        //! The new instance of type T* is constructed and initialized with values stored in the Setting element[index]
414        //!
415        //! If there does not exist  any sub-element indexed by index, and settingPresence is optional, an empty bdm::shared_ptr<T> is returned.
416        //! When settingPresence is compulsory, the returned bdm::shared_ptr<T> is never empty (an exception is thrown when the object isn't found).
417        template<class T>
418        static bdm::shared_ptr<T> build ( const Setting &element, const int index, SettingPresence settingPresence = optional ) {
419                if ( element.getLength() <= index ) {
420                        if ( settingPresence == optional )
421                                return bdm::shared_ptr<T>();
422                        else {
423                                stringstream stream;
424                                stream << index;
425                                throw UISettingException ( "UIException: the compulsory Setting with the index " + stream.str() + " is missing.", element );
426                        }
427                }
428
429                bdm::shared_ptr<T> instance;
430                from_setting<T> ( instance, to_child_setting ( element, index ) );
431                return instance;
432        }
433
434//! The new instance of type T* is constructed and initialized with values stored in the given Setting
435//!
436//! Handy in mex files. Use with care.
437        template<class T>
438        static bdm::shared_ptr<T> build ( const Setting &element, SettingPresence settingPresence = optional ) {
439                bdm::shared_ptr<T> instance;
440                from_setting<T> ( instance,  element );
441                return instance;
442        }
443
444        //!@}
445
446        //! \name Initialization of structures
447        //!@{
448        //! The type T has to be int, double, string, vec, ivec or mat.
449
450        //! The existing instance of type T is initialized with values stored in the Setting element[name]
451        //! If there does not exist any sub-element named name, this method returns false.
452        template<class T> static bool get ( T &instance, const Setting &element, const string &name, SettingPresence settingPresence = optional ) {
453                if ( !element.exists ( name ) ) {
454                        if ( settingPresence == optional )
455                                return false;
456                        else
457                                throw UISettingException ( "UIException: the compulsory Setting named \"" + name + "\" is missing.", element );
458                }
459
460                from_setting ( instance, to_child_setting ( element, name ) );
461                return true;
462        }
463
464        //! The existing instance of type T is initialized with values stored in the Setting element[index]
465        //! If there does not exist any sub-element indexed by index, this method returns false.
466        template<class T> static bool get ( T &instance, const Setting &element, const int index, SettingPresence settingPresence = optional ) {
467                if ( element.getLength() <= index ) {
468                        if ( settingPresence == optional )
469                                return false;
470                        else {
471                                stringstream stream;
472                                stream << "UIException: the compulsory Setting with the index " << index << " is missing.";
473                                stream << index;
474                                throw UISettingException ( stream.str(), element );
475                        }
476                }
477
478                from_setting ( instance, to_child_setting ( element, index ) );
479                return true;
480        }
481
482        //! The existing instance of type T is initialized with values stored in the Setting element directly
483        template<class T> static bool get ( T &instance, const Setting &element ) {
484                from_setting ( instance, element );
485                return true;
486        }
487        //!@}
488
489        //! \name Initialization of arrays Array<T>
490        //!@{
491        //! The type T has to be int, double, string, vec, ivec or mat, or pointer to any root descendant.
492
493        //! The existing array of type T is initialized with values stored in the Setting element[name]
494        //! If there is not any sub-element named name, this method returns false.
495        template<class T> static bool get ( Array<T> &array_to_load, const Setting &element, const string &name, SettingPresence settingPresence = optional ) {
496                if ( !element.exists ( name ) ) {
497                        if ( settingPresence == optional )
498                                return false;
499                        else
500                                throw UISettingException ( "UIException: the compulsory Setting named \"" + name + "\" is missing.", element );
501                }
502
503                from_setting ( array_to_load, to_child_setting ( element, name ) );
504                return true;
505        }
506
507        //! The existing array of type T is initialized with values stored in the Setting element[index]
508        //! If there is not any sub-element indexed by index, this method returns false.
509        template<class T> static bool get ( Array<T> &array_to_load, const Setting &element, const int index, SettingPresence settingPresence = optional ) {
510                if ( element.getLength() <= index ) {
511                        if ( settingPresence == optional )
512                                return bdm::shared_ptr<T>();
513                        else {
514                                stringstream stream;
515                                stream << index;
516                                throw UISettingException ( "UIException: the compulsory Setting with the index " + stream.str() + " is missing.", element );
517                        }
518                }
519
520                from_setting ( array_to_load, to_child_setting ( element, index ) );
521                return true;
522        }
523
524        //! The existing array of type T is initialized with values stored in the Setting element
525        template<class T> static bool get ( Array<T> &array_to_load, const Setting &element ) {
526                from_setting ( array_to_load, element );
527                return true;
528        }
529        //!@}
530
531        //! \name Serialization of objects and structures into a new Setting
532        //!@{
533        //! The new child Setting can be accessed either by its name - if some name is passed as a parameter -
534        //! or by its integer index. In that case, the new element is added at the very end of the current list of child Settings.
535
536        //! A root descendant is stored in the new child Setting appended to the passed element
537        static void save ( const root &instance, Setting &element, const string &name = "" );
538
539        //! A pointer to root descendant is stored in the new child Setting appended to the passed element
540        static void save ( const root * const instance, Setting &element, const string &name = "" );
541
542        //! A shared pointer to root descendant is stored in the new child Setting appended to the passed element
543        template< class T> static void save ( const bdm::shared_ptr<T> &instance, Setting &element, const string &name = "" ) {
544                save( instance.get(), element, name );
545        }
546
547        //! An Array<T> instance is stored in the new child Setting appended to the passed element
548        template<class T> static void save ( const Array<T> &array_to_save, Setting &element, const string &name = "" ) {
549                assert_type ( element, Setting::TypeGroup );
550                Setting &list = ( name == "" ) ? element.add ( Setting::TypeList )
551                                : element.add ( name, Setting::TypeList );
552                for ( int i = 0; i < array_to_save.length(); i++ )
553                        save ( array_to_save ( i ), list );
554        }
555
556        //! A matrix(of type mat) is stored in the new child Setting appended to the passed element
557        static void save ( const mat &matrix, Setting &element, const string &name = "" );
558       
559        //! A matrix(of type mat) is stored in the new child Setting appended to the passed element
560        static void save ( const ldmat &matrix, Setting &element, const string &name = "" );
561       
562        //! An integer vector (of type ivec) is stored in the new child Setting appended to the passed element
563        static void save ( const ivec &vec, Setting &element, const string &name = "" );
564
565        //! A double vector (of type vec) is stored in the new child Setting appended to the passed element
566        static void save ( const vec &vector, Setting &element, const string &name = "" );
567
568        //! A string is stored in the new child Setting appended to the passed element
569        static void save ( const string &str, Setting &element, const string &name = "" );
570
571        //! An integer is stored in the new child Setting appended to the passed element
572        static void save ( const int &integer, Setting &element, const string &name = "" );
573
574        //! A double is stored in the new child Setting appended to the passed element
575        static void save ( const double &real, Setting &element, const string &name = "" );
576
577    // The only difference from classical UserInfo approach here is the fact
578        // that the actual UI element related to log_level_base could possibly exists yet. In this case,
579        // we do not want to throw an exception as usually. We rather append current data into the existig element. This way, it is
580        // possible that more instances of log_level_base  (templated with different classes)
581        // can be stored in only one line in a configuration file
582        static void save ( const log_level_base &log_level, Setting &element );
583
584        //!@}
585
586        //! for future use
587        virtual ~UI() {}
588};
589
590
591//! The only UI descendant class which is not intended for direct use. It should be accessed within the UIREGISTER macro only.
592//! \ref ui
593template<typename T> class ParticularUI : private UI {
594public:
595        //! Constructor used by the UIREGISTER macro.
596        ParticularUI<T> ( const string &class_name ) : UI ( class_name, &typeid ( T ) ) {};
597
598        //! A method returning a brand new instance of class T, this method is the reason why there have to be a parameterless constructor in class T
599        root* new_instance() const {
600                return new T();
601        }
602};
603
604}
605
606/*!
607  \def UIREGISTER(class_name)
608  \brief Macro for registration of class into map of user-infos, registered class is scriptable using UI static methods
609
610  Argument \a class_name has to be a descendant of root class and also to have a default constructor.
611  This macro should be used in header file, immediately after a class declaration.
612
613  \ref ui
614*/
615#ifndef BDMLIB
616#define UIREGISTER(class_name) static bdm::ParticularUI<class_name> UI##class_name(#class_name)
617#else
618#define UIREGISTER(class_name)
619#endif
620
621//! Instrumental macro for UIREGISTER2
622#define QUOTEME(x) #x
623
624/*!
625  \def UIREGISTER2(class_name,template_name)
626  \brief Variant of UIREGISTER for templated classes
627
628  Technical meann of registering UIREGISTER(class_name<template_name>).
629
630  \ref ui
631 */
632#ifndef BDMLIB
633#define UIREGISTER2(class_name, temp_name) static bdm::ParticularUI<class_name<temp_name> > UI##class_name##_##temp_name( QUOTEME(class_name<temp_name>) )
634#else
635#define UIREGISTER2(class_name,temp_name)
636#endif
637
638#endif // #ifndef USER_INFO_H
Note: See TracBrowser for help on using the browser.