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

Revision 535, 21.7 kB (checked in by smidl, 15 years ago)

Changed UIREGISTER for templated classes to UIREGISTER2.

* it is now consistent with SHAREDPTR2
* it works even when two UIREGISTERS are on the same line in defferent files

  • 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        //! 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        //! Enum 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, 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).
385        template<class T>
386        static shared_ptr<T> build ( const Setting &element, const int index, SettingPresence settingPresence = optional ) {
387                if ( element.getLength() <= index ) {
388                        if ( settingPresence == optional )
389                                return shared_ptr<T>();
390                        else {
391                                stringstream stream;
392                                stream << index;
393                                throw UIException ( "the compulsory Setting with the index " + stream.str() + " is missing", element );
394                        }
395                }
396
397                shared_ptr<T> instance;
398                from_setting<T> ( instance, to_child_setting ( element, index ) );
399                return instance;
400        }
401
402        //!@}
403
404        //! \name Initialization of structures
405        //!@{
406        //! The type T has to be int, double, string, vec, ivec or mat.
407
408        //! The existing instance of type T is initialized with values stored in the Setting element[name]
409        //! If there is not any sub-element named #name, this method returns false.
410        template<class T> static bool get ( T &instance, const Setting &element, const string &name, SettingPresence settingPresence = optional ) {
411                if ( !element.exists ( name ) ) {
412                        if ( settingPresence == optional )
413                                return false;
414                        else
415                                throw UIException ( "the compulsory Setting named \"" + name + "\" is missing", element );
416                }
417
418                from_setting ( instance, to_child_setting ( element, name ) );
419                return true;
420        }
421
422        //! The existing instance of type T is initialized with values stored in the Setting element[index]
423        //! If there is not any sub-element indexed by #index, this method returns false.
424        template<class T> static bool get ( T &instance, const Setting &element, const int index, SettingPresence settingPresence = optional ) {
425                if ( element.getLength() <= index ) {
426                        if ( settingPresence == optional )
427                                return false;
428                        else {
429                                stringstream stream;
430                                stream << index;
431                                throw UIException ( "the compulsory Setting with the index " + stream.str() + " is missing", element );
432                        }
433                }
434
435                from_setting ( instance, to_child_setting ( element, index ) );
436                return true;
437        }
438
439        //! The existing instance of type T is initialized with values stored in the Setting element directly
440        template<class T> static bool get ( T &instance, const Setting &element ) {
441                from_setting ( instance, element );
442                return true;
443        }
444        //!@}
445
446        //! \name Initialization of arrays Array<T>
447        //!@{
448        //! The type T has to be int, double, string, vec, ivec or mat, or pointer to any root descendant.
449
450        //! The existing array of type T is initialized with values stored in the Setting element[name]
451        //! If there is not any sub-element named #name, this method returns false.
452        template<class T> static bool get ( Array<T> &array_to_load, const Setting &element, const string &name, SettingPresence settingPresence = optional ) {
453                if ( !element.exists ( name ) )
454                        return false;
455
456                from_setting ( array_to_load, to_child_setting ( element, name ) );
457                return true;
458        }
459
460        //! The existing array of type T is initialized with values stored in the Setting element[index]
461        //! If there is not any sub-element indexed by #index, this method returns false.
462        template<class T> static bool get ( Array<T> &array_to_load, const Setting &element, const int index, SettingPresence settingPresence = optional ) {
463                if ( element.getLength() <= index )
464                        return false;
465
466                from_setting ( array_to_load, to_child_setting ( element, index ) );
467                return true;
468        }
469
470        //! The existing array of type T is initialized with values stored in the Setting element
471        template<class T> static bool get ( Array<T> &array_to_load, const Setting &element ) {
472                from_setting ( array_to_load, element );
473                return true;
474        }
475        //!@}
476
477        //! \name Serialization of objects and structures into a new Setting
478        //!@{
479        //! The new child Setting can be accessed either by its name - if some name is passed as a parameter -
480        //! or by its integer index. In that case, the new element is added at the very end of the current list of child Settings.
481
482        //! A root descendant instance is stored in the new child Setting appended to the passed element
483        template< class T> static void save ( const T * const instance, Setting &element, const string &name = "" ) {
484                Setting &set = ( name == "" ) ? element.add ( Setting::TypeGroup )
485                               : element.add ( name, Setting::TypeGroup );
486
487                const string &class_name = MappedUI::retrieve_class_name ( &typeid ( *instance ) );
488
489                // add attribute "class"
490                Setting &type = set.add ( "class", Setting::TypeString );
491                type = class_name;
492
493                try {
494                        instance->to_setting ( set );
495                } catch ( SettingException sttng_xcptn ) {
496                    string msg = "the method ";
497                    msg += class_name;
498                    msg += ".to_setting(Setting&) has thrown a SettingException. Try to correct this method.";
499                    throw UIException(msg, sttng_xcptn.getPath());
500                }
501        }
502
503        template< class T> static void save ( const shared_ptr<T> &instance, Setting &element, const string &name = "" ) {
504                save<T> ( instance.get(), element, name );
505        }
506
507        //! An Array<T> instance is stored in the new child Setting appended to the passed element
508        template<class T> static void save ( const Array<T> &array_to_save, Setting &element, const string &name = "" ) {
509                assert_type ( element, Setting::TypeGroup );
510                Setting &list = ( name == "" ) ? element.add ( Setting::TypeList )
511                                : element.add ( name, Setting::TypeList );
512                for ( int i = 0; i < array_to_save.length(); i++ )
513                        save ( array_to_save ( i ), list );
514        }
515
516        //! A matrix(of type mat) is stored in the new child Setting appended to the passed element
517        static void save ( const mat &matrix, Setting &element, const string &name = "" );
518
519        //! An integer vector (of type ivec) is stored in the new child Setting appended to the passed element
520        static void save ( const ivec &vec, Setting &element, const string &name = "" );
521
522        //! A double vector (of type vec) is stored in the new child Setting appended to the passed element
523        static void save ( const vec &vector, Setting &element, const string &name = "" );
524
525        //! A string is stored in the new child Setting appended to the passed element
526        static void save ( const string &str, Setting &element, const string &name = "" );
527
528        //! An integer is stored in the new child Setting appended to the passed element
529        static void save ( const int &integer, Setting &element, const string &name = "" );
530
531        //! A double is stored in the new child Setting appended to the passed element
532        static void save ( const double &real, Setting &element, const string &name = "" );
533        //!@}
534
535};
536
537
538//! The only UI descendant class which is not intended for direct use. It should be accessed within the UIREGISTER macro only.
539//! \ref ui_page
540template<typename T> class ParticularUI : private UI {
541public:
542        //! Constructor used by the UIREGISTER macro.
543        ParticularUI<T> ( const string &class_name ) : UI ( class_name, &typeid ( T ) ) {};
544
545        //! 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
546        root* new_instance() const {
547                return new T();
548        }
549};
550
551}
552
553/*!
554  \def UIREGISTER(class_name)
555  \brief Macro for registration of class into map of user-infos, registered class is scriptable using UI static methods
556
557  Argument \a class_name has to be a descendant of root class and also to have a default constructor.
558  This macro should be used in header file, immediately after a class declaration.
559
560  \ref ui_page
561*/
562#ifndef BDMLIB
563#define UIREGISTER(class_name) static bdm::ParticularUI<class_name> UI##class_name(#class_name)
564#else
565#define UIREGISTER(class_name)
566#endif
567
568//! Instrumental macro for UIREGISTER2
569#define QUOTEME(x) #x
570
571/*!
572  \def UIREGISTER2(class_name,template_name)
573  \brief Variant of UIREGISTER for templated classes
574
575  Technical meann of registering UIREGISTER(class_name<template_name>).
576
577  \ref ui_page
578 */
579#ifndef BDMLIB
580#define UIREGISTER2(class_name, temp_name) static bdm::ParticularUI<class_name<temp_name> > UI##class_name##_##temp_name( QUOTEME(class_name<temp_name>) )
581#else
582#define UIREGISTER2(class_name,temp_name)
583#endif
584
585#endif // #ifndef USER_INFO_H
Note: See TracBrowser for help on using the browser.