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
Line 
1/*!
2  \file
3  \brief UI (user info) class for loading/saving objects from/to configuration files.
4  It is designed with use of libconfig C/C++ Configuration File Library
5  \ref ui_page
6  \author Vaclav Smidl.
7
8  -----------------------------------
9  BDM++ - C++ library for Bayesian Decision Making under Uncertainty
10
11  Using IT++ for numerical operations
12  -----------------------------------
13*/
14
15#ifndef USER_INFO_H
16#define USER_INFO_H
17
18#include <stdio.h>
19#include <string>
20#include <typeinfo>
21#include <map>
22#include <stdexcept>
23
24#include "libconfig/libconfig.h++"
25#include "../bdmroot.h"
26#include "itpp/itbase.h"
27
28
29using std::string;
30using namespace std;
31using namespace libconfig;
32
33namespace bdm {
34
35//! Exception prepared for reporting user-info errors which are always related to some concrete Setting path
36//!
37//!  \ref ui_page
38class UIException : public std::exception {
39
40public:
41        //! Error message
42        const string message;
43
44        //! Path to the problematic setting
45        const string path;
46
47private:
48        string what_message;
49
50public:
51        //! Use this constructor when you can pass the problematical Setting as a parameter
52        UIException ( const string &message, const Setting &element )
53                        : message ( "UI error: " + message + "." ), path ( "Check path \"" + string ( element.getPath() ) + "\"." ) {
54                init_what_message();
55        }
56
57        //! This constructor is for other occasions, when only path of problematical Setting is known
58        UIException ( const string &message, const string &path )
59                        : message ( "UI error: " + message + "." ), path ( "Check path \"" + path + "\"." ) {
60                init_what_message();
61        }
62
63        //! Overriden method for reporting an error message
64        virtual const char* what() const throw() {
65                return what_message.c_str();
66        }
67
68        ~UIException() throw() {};
69
70private:
71        void init_what_message() {
72                what_message = message;
73                what_message += ' ';
74                what_message += path;
75        }
76};
77
78
79/*!
80@brief This class serves to load and/or save user-infos into/from
81configuration files stored on a hard-disk.
82
83Firstly, save some user-infos into the new UIFile instance. Then,
84call the save method with a filename as its only argument:
85
86\code
87        CAudi audi;
88        UIFile file;
89        UI::save( audi, file, "TT");
90        file.save("cars.cfg");
91\endcode
92
93In the other way round, when loading object from a configuration file,
94the appropriate code looks like this:
95
96\code
97        UIFile file("cars.cfg");
98        CAudi *audi = UI::build<CAudi>(file,"TT");
99\endcode
100
101\ref ui_page
102*/
103class UIFile : public Config {
104public:
105        //! Create empty file instance prepared to store Settings
106        UIFile();
107
108        //! Creates instance and fills it from the configuration file file_name
109        UIFile ( const string &file_name );
110
111        //! Save all the stored Settings into the configuration file file_name
112        void save ( const string &file_name );
113
114        //! This operator allows the ability of substituting Setting parameter by UIFile instance
115        operator Setting&();
116};
117
118/*!
119@brief This class serves to expand links used within configuration files.
120
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
123configuration file.
124
125Link have three parts, \<name\> : \<path\> \<\@filename\>. Field \<name\> contains the
126name of the new setting, \<path\> is the relative path to the referenced setting, which
127has to be taken from the %root Setting element. The last part \<\@filename\> is optional,
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    ...
133        jardovo :
134        {
135          class = "Car";
136          year = 1992;
137          manufacturer = "liaz";
138          kilometers = 1555000;
139        };
140        ondrejovo :
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        };
148
149        #this is the example of local link to another mean of transport
150        elisky = "jardovo";
151
152        ...
153
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".
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
175The whole point is that a resolved link (class member #result, i.e., "link.result" in the previous example) could point
176into a different configuration file. In that case there has to be an UIFile instance managing reading from this
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,
180that the #result Setting reference is valid within the scope of SettingResolver instance.
181
182\ref ui_page
183 */
184class SettingResolver : root {
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        //!
191        //! This code could be also located directly in constructor. The only reason why we made this
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.
194        const Setting &initialize_reference ( UIFile* &file, const Setting &potential_link );
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.
201        SettingResolver ( const Setting &potential_link );
202
203        //! An opened UIFile file is closed here if necessary.
204        ~SettingResolver();
205};
206
207/*!
208@brief UI is an abstract class which collects all the auxiliary functions useful to prepare some concrete
209user-infos.
210
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
216*/
217class UI {
218private:
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
220        //!
221        //! The key property of this class is that it initializes the internal maps on global init,
222        //! before the instance is used for a first time. Therefore, we do not have to care about initialization
223        //! during a call of UIREGISTER macro operating with both these mappings.
224        class MappedUI {
225        private:
226                //! Type definition of mapping which transforms class names to the related UI instances
227                typedef map< const string, const UI* const > StringToUIMap;
228
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;
231
232                //! Immediately initialized instance of type StringToUIMap
233                static StringToUIMap& mapped_strings();
234
235                //! Immediately initialized instance of type TypeInfoToStringMap
236                static TypeInfoToStringMap& mapped_type_infos();
237
238                //! Method for reporting a error when an attempt to operate with an unregistered class occures
239                static void unregistered_class_error ( const string &unregistered_class_name );
240
241        public:
242                //! Add a pair key-userinfo into the internal map
243                static void add_class ( const string &class_name, const type_info * const class_type_info, const UI* const ui );
244
245                //! Search for an userinfo related to the passed class name within the internal map
246                static const UI& retrieve_ui ( const string &class_name );
247
248                //! Search for an class name related to the passed type_info within the internal map
249                static const string& retrieve_class_name ( const type_info* const class_type_info );
250        };
251
252        //! Function assertting that the setting element is of the SettingType type
253        static void assert_type ( const Setting &element, Setting::Type type );
254
255        //! Method assembling a typeless instance, it is implemented in descendant class ParticularUI<T>
256        virtual root* new_instance() const = 0;
257
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 );
260
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 );
266        //! This method converts a Setting into an integer vector
267        static void from_setting ( ivec &vector, const Setting &element );
268        //! This method converts a Setting into a string
269        static void from_setting ( string &str, const Setting &element );
270        //! This method converts a Setting into a real vector
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 );
276        //! This method converts a Setting into a class T descendant
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 );
280
281                // we get a value stored in the "class" attribute
282                string class_name;
283                if ( !link.result.lookupValue ( "class", class_name ) )
284                        throw UIException ( "the obligatory \"class\" identifier is missing", link.result );
285
286                // then we find a user-info related to this type
287                const UI& related_UI = MappedUI::retrieve_ui ( class_name );
288
289                root* typeless_instance = related_UI.new_instance();
290
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() + "\".";
299                        throw UIException(msg, link.result);
300                }
301        }
302
303        //! This methods converts a Setting into a new templated array of type Array<T>
304        template<class T> static void from_setting ( Array<T> &array_to_load, const Setting &element ) {
305                const SettingResolver link ( element );
306
307                assert_type ( link.result, Setting::TypeList );
308
309                int len = link.result.getLength();
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] );
315        }
316
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.
321        template<class T> static void from_setting ( T &variable_to_load, const Setting &element ) {
322                throw UIException ( "from_setting is not implemented for this type", element );
323        }
324
325
326protected:
327        //! Default constructor for internal use only, see \sa ParticularUI<T>
328        UI ( const string& class_name, const type_info * const class_type_info ) {
329                MappedUI::add_class ( class_name, class_type_info, this );
330        }
331
332public:
333
334        //! Enumerical type used to determine whether the data for concrete Settingis is compulsory or optional
335        enum SettingPresence { optional, compulsory } ;
336
337        //! \name Initialization of classes
338        //!@{
339        //! The type T has to be a #bdm::root descendant class
340
341        //! The new instance of type T* is constructed and initialized with values stored in the Setting element[name]
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 ) {
345                if ( !element.exists ( name ) ) {
346                        if ( settingPresence == optional )
347                                return NULL;
348                        else
349                                throw UIException ( "the compulsory Setting named \"" + name + "\" is missing", element );
350                }
351
352                T* instance;
353                from_setting<T> ( instance, to_child_setting ( element, name ) );
354                return instance;
355        }
356
357        //! The new instance of type T* is constructed and initialized with values stored in the Setting element[index]
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 ) {
361                if ( element.getLength() <= index ) {
362                        if ( settingPresence == optional )
363                                return NULL;
364                        else {
365                                stringstream stream;
366                                stream << index;
367                                throw UIException ( "the compulsory Setting with the index " + stream.str() + " is missing", element );
368                        }
369                }
370
371                T* instance;
372                from_setting<T> ( instance, to_child_setting ( element, index ) );
373                return instance;
374        }
375
376        //! The new instance of type T* is constructed and initialized with values stored in the Setting element
377        template<class T> static T* build ( const Setting &element ) {
378                T* instance;
379                from_setting<T> ( instance,  element );
380                return instance;
381
382        }
383
384        //!@}
385
386        //! \name Initialization of structures
387        //!@{
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]
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 ) {
393                if ( !element.exists ( name ) ) {
394                        if ( settingPresence == optional )
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;
402        }
403
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 ) {
407                if ( element.getLength() <= index ) {
408                        if ( settingPresence == optional )
409                                return false;
410                        else {
411                                stringstream stream;
412                                stream << index;
413                                throw UIException ( "the compulsory Setting with the index " + stream.str() + " is missing", element );
414                        }
415                }
416
417                from_setting ( instance, to_child_setting ( element, index ) );
418                return true;
419        }
420
421        //! The existing instance of type T is initialized with values stored in the Setting element directly
422        template<class T> static bool get ( T &instance, const Setting &element ) {
423                from_setting ( instance, element );
424                return true;
425        }
426        //!@}
427
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]
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;
440        }
441
442        //! The existing array of type T is initialized with values stored in the Setting element[index]
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;
450        }
451
452        //! The existing array of type T is initialized with values stored in the Setting element
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;
456        }
457        //!@}
458
459        //! \name Serialization of objects and structures into a new Setting
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
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 );
468
469                const string &class_name = MappedUI::retrieve_class_name ( &typeid ( *instance ) );
470
471                // add attribute "class"
472                Setting &type = set.add ( "class", Setting::TypeString );
473                type = class_name;
474
475                try {
476                        instance->to_setting ( set );
477                } catch ( SettingException sttng_xcptn ) {
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());
482                }
483        }
484
485        //! An Array<T> instance is stored in the new child Setting appended to the passed element
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 );
492        }
493
494        //! A matrix(of type mat) is stored in the new child Setting appended to the passed element
495        static void save ( const mat &matrix, Setting &element, const string &name = "" );
496
497        //! An integer vector (of type ivec) is stored in the new child Setting appended to the passed element
498        static void save ( const ivec &vec, Setting &element, const string &name = "" );
499
500        //! A double vector (of type vec) is stored in the new child Setting appended to the passed element
501        static void save ( const vec &vector, Setting &element, const string &name = "" );
502
503        //! A string is stored in the new child Setting appended to the passed element
504        static void save ( const string &str, Setting &element, const string &name = "" );
505
506        //! An integer is stored in the new child Setting appended to the passed element
507        static void save ( const int &integer, Setting &element, const string &name = "" );
508
509        //! A double is stored in the new child Setting appended to the passed element
510        static void save ( const double &real, Setting &element, const string &name = "" );
511        //!@}
512
513};
514
515
516//! The only UI descendant class which is not intended for direct use. It should be accessed within the ::UIREGISTER macro only.
517//! \ref ui_page
518template<typename T> class ParticularUI : private UI {
519private:
520        //! Default constructor, which is intentionally declared as private
521        ParticularUI<T> ( const string &class_name ) : UI ( class_name, &typeid ( T ) ) {};
522
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
525        static const ParticularUI<T>& factory;
526
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
528        root* new_instance() const {
529                return new T();
530        }
531};
532
533}
534
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
550#endif // #ifndef USER_INFO_H
Note: See TracBrowser for help on using the browser.