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

Revision 478, 20.3 kB (checked in by mido, 15 years ago)

patch preklepu, uz by mel jit build knihovny

  • 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.
[477]167
[471]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>
[478]290        template<class T> static void from_setting ( Array<T> &array_to_load, const Setting &element ) {
[471]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.
[478]307        template<class T> static void from_setting ( T &variable_to_load, const Setting &element ) {
[477]308                throw UIException ( "from_setting is not implemented for this type", element );
[471]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
[477]320        //! Enumerical type used to determine whether the data for concrete Settingis is compulsory or optional
[471]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 ) {
[477]331                if ( !element.exists ( name ) ) {
332                        if ( settingPresence == optional )
[471]333                                return NULL;
334                        else
335                                throw UIException ( "the compulsory Setting named \"" + name + "\" is missing", element );
336                }
337
[396]338                T* instance;
[471]339                from_setting<T> ( instance, to_child_setting ( element, name ) );
[396]340                return instance;
341        }
[471]342
[396]343        //! The new instance of type T* is constructed and initialized with values stored in the Setting element[index]
[471]344        //!
345        //! If there is not any sub-element indexed by #index, the null pointer is returned.
346        template<class T> static T* build ( const Setting &element, const int index, SettingPresence settingPresence = optional ) {
[477]347                if ( element.getLength() <= index ) {
348                        if ( settingPresence == optional )
[471]349                                return NULL;
[477]350                        else {
[471]351                                stringstream stream;
352                                stream << index;
[477]353                                throw UIException ( "the compulsory Setting with the index " + stream.str() + " is missing", element );
[471]354                        }
355                }
356
[351]357                T* instance;
[471]358                from_setting<T> ( instance, to_child_setting ( element, index ) );
[351]359                return instance;
[344]360        }
[471]361
[396]362        //! The new instance of type T* is constructed and initialized with values stored in the Setting element
[471]363        template<class T> static T* build ( const Setting &element ) {
[388]364                T* instance;
[471]365                from_setting<T> ( instance,  element );
[388]366                return instance;
[471]367
[388]368        }
[471]369
[394]370        //!@}
[281]371
[471]372        //! \name Initialization of structures
[394]373        //!@{
[396]374        //! The type T has to be int, double, string, vec, ivec or mat.
375
376        //! The existing instance of type T is initialized with values stored in the Setting element[name]
[471]377        //! If there is not any sub-element named #name, this method returns false.
378        template<class T> static bool get ( T &instance, const Setting &element, const string &name, SettingPresence settingPresence = optional ) {
[477]379                if ( !element.exists ( name ) ) {
380                        if ( settingPresence == optional )
[471]381                                return false;
382                        else
383                                throw UIException ( "the compulsory Setting named \"" + name + "\" is missing", element );
384                }
385
386                from_setting ( instance, to_child_setting ( element, name ) );
387                return true;
[344]388        }
389
[471]390        //! The existing instance of type T is initialized with values stored in the Setting element[index]
391        //! If there is not any sub-element indexed by #index, this method returns false.
392        template<class T> static bool get ( T &instance, const Setting &element, const int index, SettingPresence settingPresence = optional ) {
[477]393                if ( element.getLength() <= index ) {
394                        if ( settingPresence == optional )
[471]395                                return false;
[477]396                        else {
[471]397                                stringstream stream;
398                                stream << index;
[477]399                                throw UIException ( "the compulsory Setting with the index " + stream.str() + " is missing", element );
[471]400                        }
401                }
402
403                from_setting ( instance, to_child_setting ( element, index ) );
404                return true;
[344]405        }
406
[396]407        //! The existing instance of type T is initialized with values stored in the Setting element directly
[471]408        template<class T> static bool get ( T &instance, const Setting &element ) {
409                from_setting ( instance, element );
410                return true;
[394]411        }
[396]412        //!@}
[394]413
[396]414        //! \name Initialization of arrays Array<T>
415        //!@{
416        //! The type T has to be int, double, string, vec, ivec or mat, or pointer to any root descendant.
417
418        //! The existing array of type T is initialized with values stored in the Setting element[name]
[471]419        //! If there is not any sub-element named #name, this method returns false.
420        template<class T> static bool get ( Array<T> &array_to_load, const Setting &element, const string &name, SettingPresence settingPresence = optional ) {
421                if ( !element.exists ( name ) )
422                        return false;
423
424                from_setting ( array_to_load, to_child_setting ( element, name ) );
425                return true;
[344]426        }
427
[396]428        //! The existing array of type T is initialized with values stored in the Setting element[index]
[471]429        //! If there is not any sub-element indexed by #index, this method returns false.
430        template<class T> static bool get ( Array<T> &array_to_load, const Setting &element, const int index, SettingPresence settingPresence = optional ) {
431                if ( element.getLength() <= index )
432                        return false;
433
434                from_setting ( array_to_load, to_child_setting ( element, index ) );
435                return true;
[344]436        }
437
[396]438        //! The existing array of type T is initialized with values stored in the Setting element
[471]439        template<class T> static bool get ( Array<T> &array_to_load, const Setting &element ) {
440                from_setting ( array_to_load, element );
441                return true;
[394]442        }
443        //!@}
444
[471]445        //! \name Serialization of objects and structures into a new Setting
[396]446        //!@{
447        //! The new child Setting can be accessed either by its name - if some name is passed as a parameter -
448        //! or by its integer index. In that case, the new element is added at the very end of the current list of child Settings.
449
450        //! A root descendant instance is stored in the new child Setting appended to the passed element
[471]451        template< class T> static void save ( const T * const instance, Setting &element, const string &name = "" ) {
452                Setting &set = ( name == "" ) ? element.add ( Setting::TypeGroup )
453                               : element.add ( name, Setting::TypeGroup );
[344]454
[471]455                const string &class_name = MappedUI::retrieve_class_name ( &typeid ( *instance ) );
456
457                // add attribute "class"
458                Setting &type = set.add ( "class", Setting::TypeString );
[351]459                type = class_name;
[344]460
[471]461                try {
462                        instance->to_setting ( set );
463                } catch ( SettingException sttng_xcptn ) {
[474]464                    string msg = "the method ";
465                    msg += class_name;
466                    msg += ".to_setting(Setting&) has thrown a SettingException. Try to correct this method.";
467                    throw UIException(msg, sttng_xcptn.getPath());
[351]468                }
[344]469        }
470
[396]471        //! An Array<T> instance is stored in the new child Setting appended to the passed element
[471]472        template<class T> static void save ( const Array<T> &array_to_save, Setting &element, const string &name = "" ) {
473                assert_type ( element, Setting::TypeGroup );
474                Setting &list = ( name == "" ) ? element.add ( Setting::TypeList )
475                                : element.add ( name, Setting::TypeList );
476                for ( int i = 0; i < array_to_save.length(); i++ )
477                        save ( array_to_save ( i ), list );
[344]478        }
[351]479
[396]480        //! A matrix(of type mat) is stored in the new child Setting appended to the passed element
[471]481        static void save ( const mat &matrix, Setting &element, const string &name = "" );
[351]482
[396]483        //! An integer vector (of type ivec) is stored in the new child Setting appended to the passed element
[471]484        static void save ( const ivec &vec, Setting &element, const string &name = "" );
485
[396]486        //! A double vector (of type vec) is stored in the new child Setting appended to the passed element
[471]487        static void save ( const vec &vector, Setting &element, const string &name = "" );
[396]488
489        //! A string is stored in the new child Setting appended to the passed element
[471]490        static void save ( const string &str, Setting &element, const string &name = "" );
[351]491
[396]492        //! An integer is stored in the new child Setting appended to the passed element
[471]493        static void save ( const int &integer, Setting &element, const string &name = "" );
[394]494
[396]495        //! A double is stored in the new child Setting appended to the passed element
[471]496        static void save ( const double &real, Setting &element, const string &name = "" );
[396]497        //!@}
[394]498
[281]499};
500
501
[396]502//! The only UI descendant class which is not intended for direct use. It should be accessed within the ::UIREGISTER macro only.
[471]503//! \ref ui_page
504template<typename T> class ParticularUI : private UI {
[396]505private:
506        //! Default constructor, which is intentionally declared as private
[471]507        ParticularUI<T> ( const string &class_name ) : UI ( class_name, &typeid ( T ) ) {};
[344]508
[396]509public:
510        //! The only instance of this class (each type T has its own instance) which is used as a factory for processing related UI
[471]511        static const ParticularUI<T>& factory;
[344]512
[396]513        //! 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]514        root* new_instance() const {
[344]515                return new T();
516        }
[281]517};
518
[344]519}
520
[471]521/*!
522  \def UIREGISTER(class_name)
523  \brief Macro for registration of class into map of user-infos, registered class is scriptable using UI static methods
524
525  Argument \a class_name has to be a descendant of root class and also, it has to have parameterless constructor prepared.
526  This macro should be used in header file, immediately after a class declaration.
527
528  \ref ui_page
529*/
530#ifndef BDMLIB
531#define UIREGISTER(class_name) template<> const ParticularUI<class_name>& ParticularUI<class_name>::factory = ParticularUI<class_name>(#class_name)
532#else
533#define UIREGISTER(class_name)
534#endif
535
[358]536#endif // #ifndef USER_INFO_H
Note: See TracBrowser for help on using the browser.