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

Revision 474, 20.3 kB (checked in by vbarta, 15 years ago)

reverted settings load & save to throw UIException (std::exception usage didn't compile with gcc 4.2.3)

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