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

Revision 471, 21.4 kB (checked in by mido, 15 years ago)

1) ad UserInfo?: UI::get a UI::build predelany tak, ze vraci fals / NULL v pripade neexistence pozadovaneho Settingu, pridana enumericky typ UI::SettingPresence?, predelany stavajici UI implementace, dodelana UI dokumentace
2) dokoncena konfigurace ASTYLERU, brzy bude aplikovan
3) doxygen nastaven tak, ze vytvari soubor doxy_warnings.txt

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