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

Revision 945, 23.8 kB (checked in by mido, 14 years ago)

patch of UI and log_levels to obtain nicer and mainly compilable source code.)

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