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

Revision 483, 20.5 kB (checked in by vbarta, 15 years ago)

fixed UIException::what returning invalid (by the time it returns) pointer

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