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

Revision 529, 21.4 kB (checked in by vbarta, 15 years ago)

defined *_ptr wrappers of shared pointers

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