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

Revision 417, 17.4 kB (checked in by vbarta, 15 years ago)

fixed doc typos, using standard terminology

  • 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  \author Vaclav Smidl.
6
7  -----------------------------------
8  BDM++ - C++ library for Bayesian Decision Making under Uncertainty
9
10  Using IT++ for numerical operations
11  -----------------------------------
12*/
13
14#ifndef USER_INFO_H
15#define USER_INFO_H
16
17#include <stdio.h>
18#include <string>
19#include <typeinfo>
20#include <map>
21#include <stdexcept>
22
23#include "libconfig/libconfig.h++"
24#include "../bdmroot.h"
25#include "itpp/itbase.h"
26
27
28using std::string;
29using namespace std;
30using namespace libconfig;
31
32namespace bdm
33{
34       
35/*!
36  \def UIREGISTER(class_name)
37  \brief Macro for registration of class into map of user-infos, registered class is scriptable using UI static methods
38 
39  Argument \a class_name has to be a descendant of root class and also, it has to have parameterless constructor prepared.
40  This macro should be used in header file, immediately after a class declaration.
41 
42  \sa TODO MODUL 
43*/
44#ifndef BDMLIB
45        #define UIREGISTER(class_name) template<> const ParticularUI<class_name>& ParticularUI<class_name>::factory = ParticularUI<class_name>(#class_name)
46#else
47        #define UIREGISTER(class_name)
48#endif
49
50//! Exception prepared for reporting user-info errors which are always related to some concrete Setting path
51class UIException : public std::exception
52{
53private:
54        //! Error message
55        const string message;
56
57public:
58        //! Use this constructor when you can pass the problematical Setting as a parameter
59        UIException( const string &message, const Setting &element  ) 
60                : message( "UI error: " + message + ". Check path \"" + string(element.getPath()) + "\"." )
61        {
62        }
63
64        //! This constructor is for other occasions, when only path of problematical Setting is known
65        UIException( const string &message, const string &path  ) 
66                : message( "UI error: " + message + "! Check path \"" + path + "\"." )
67        {
68        }
69
70        //! Overriden method for reporting an error message
71        virtual const char* what() const throw()
72        {
73                return message.c_str();
74        }
75        ~UIException() throw() {};
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*/
101class UIFile : public Config
102{
103public:
104        //! Create empty file instance prepared to store Settings
105        UIFile();
106
107        //! Creates instance and fills it from the configuration file file_name
108        UIFile( const string &file_name );
109
110        //! Save all the stored Settings into the configuration file file_name
111        void save(const string &file_name);
112
113        //! This operator allows the ability of substituting Setting parameter by UIFile instance
114        operator Setting&();
115};
116
117/*!
118@brief This class serves to expand links used within configuration files.
119
120Value of any type but string can be linked to some other value of the same type
121defined elsewhere in the current configuration file or even in some different
122configuration file.
123
124Link have three parts, \<name\> : \<path\> \<\@filename\>. Field \<name\> contains the
125name of the new setting, \<path\> is the relative path to the referenced setting, which
126has to be taken from the %root Setting element. The last part \<\@filename\> is optional,
127it contains filename in the case the link should refer to a variable stored in a different
128file. From the previous part \<path\>, it has to be separated by '@'.
129
130\code
131    ...
132        jardovo :
133        {
134          class = "Car";
135          year = 1992;
136          manufacturer = "liaz";
137          kilometers = 1555000;
138        };
139        ondrejovo :
140        {
141          class = "Bike";
142          year = 1996;
143          manufacturer = "author";
144          electricLights = true;
145          matr = ( 2, 2, [ 1.0, 0.0, 0.0, 1.0 ] );
146        };
147       
148        #this is the example of local link to another mean of transport
149        elisky = "jardovo";
150
151        ...
152
153        # and this link is external link pointing to file "other_cars.cfg" stored in the
154        # same directory, in that file, it refers to the local Setting "magic_cars.skubankovo"
155        kati = "magic_cars.skubankovo@other_cars.cfg";
156
157    ...
158\endcode
159
160When you want to expand a possible linked setting "element" within your code, it has to be treated this way:
161
162\code
163        ...
164
165        const SettingResolver link( element );
166
167        ...
168
169        int len = link.result.getLength();
170
171        ...
172\endcode
173
174The whole point is that a resolved link (class member #result, i.e., "link.result" in the previous example) could point
175into a different configuration file. In that case there has to be an UIFile instance managing reading from this
176file. As the libconfig::Config deletes all its Settings when dealocated, UIFile must not be dealocated until all
177the necessary operation on the linked Setting are finished (otherwise, the link #result would be invalid just after
178the UIFile dealocation). And that is exactly the mechanism implemented within SettingResolver class. It assures,
179that the #result Setting reference is valid within the scope of SettingResolver instance.
180 */
181class SettingResolver : root
182{
183private:
184        //! If necessary, this pointer stores an addres of an opened UIFile, else it equals NULL
185        UIFile *file;
186
187        //! This method initialize #result reference, i.e., it executes the main code of SettingResolver class
188        //!
189        //! This code could be also located directly in constructor. The only reason why we made this
190        //! method is the keyword 'const' within the declaration of #result reference . Such a reference
191        //! have to be intialized before any other constructor command, exactly in the way it is implemented now.
192        const Setting &initialize_reference( UIFile* &file, const Setting &potential_link);
193
194public:
195        //! Reference to a resolved link or to the original Setting in the case it does not contain a link
196        const Setting &result;
197
198        //! If potential_link contains a link to some other setting, it is resolved here. Anyway, the Setting reference #result is prepared for use.
199        SettingResolver( const Setting &potential_link );
200       
201        //! An opened UIFile file is closed here if necessary.
202        ~SettingResolver();             
203};
204
205/*!
206@brief UI is an abstract class and it is intended for internal purposes only
207
208This class exists mainly to allow pointers to its templated descendant ParticularUI<T>. Next,
209it collects all the auxiliary functions useful to prepare some concret user-infos, see static
210methods 'build', 'get' and 'save'.
211*/
212class UI
213{
214private:
215        //! 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
216        //!
217        //! The key property of this class is that it initializes the internal maps on global init,
218        //! before the instance is used for a first time. Therefore, we do not have to care about initialization
219        //! during a call of UIREGISTER macro operating with both these mappings.
220        class MappedUI
221        {
222        private:
223                //! Type definition of mapping which transforms class names to the related UI instances
224                typedef map< const string, const UI* const > StringToUIMap;
225
226                //! Type definition of mapping which transforms RTTI type_infos to the related class names
227                typedef map< const type_info * const, const string > TypeInfoToStringMap;
228
229                //! Immediately initialized instance of type StringToUIMap
230                static StringToUIMap& mapped_strings();
231
232                //! Immediately initialized instance of type TypeInfoToStringMap
233                static TypeInfoToStringMap& mapped_type_infos();
234
235                //! Method for reporting a error when an attempt to operate with an unregistered class occures
236                static void unregistered_class_error( const string &unregistered_class_name );
237
238        public:
239                //! Add a pair key-userinfo into the internal map
240                static void add_class( const string &class_name, const type_info * const class_type_info, const UI* const ui );
241
242                //! Search for an userinfo related to the passed class name within the internal map
243                static const UI& retrieve_ui( const string &class_name );
244
245                //! Search for an class name related to the passed type_info within the internal map
246                static const string& retrieve_class_name( const type_info* const class_type_info );
247        };
248
249        //! Function assertting that the setting element is of the SettingType type
250        static void assert_type( const Setting &element, Setting::Type type);
251
252        //! Method assembling a typeless instance, it is implemented in descendant class ParticularUI<T>
253        virtual root* new_instance() const = 0;
254       
255        //! Method switching from the \a element to its child Setting according the passed \a index, it also does all the necessary error-checking
256        static const Setting& to_child_setting( const Setting &element, const int index );
257
258        //! Method switching from the \a element to its child Setting according the passed \a name, it also does all the necessary error-checking
259        static const Setting& to_child_setting( const Setting &element, const string &name );
260
261        //! This method converts a Setting into a matrix
262        static void from_setting( mat& matrix, const Setting &element );       
263        //! This method converts a Setting into an integer vector
264        static void from_setting( ivec &vector, const Setting &element );
265        //! This method converts a Setting into a string
266        static void from_setting( string &str, const Setting &element );
267        //! This method converts a Setting into a real vector
268        static void from_setting( vec &vector, const Setting &element );
269        //! This method converts a Setting into a integer scalar
270        static void from_setting( int &integer, const Setting &element );
271        //! This method converts a Setting into a real scalar
272        static void from_setting( double &real, const Setting &element );
273        //! This method converts a Setting into a class T descendant
274        template<class T> static void from_setting( T* &instance, const Setting &element )
275        {                       
276                const SettingResolver link( element );
277                assert_type(link.result,Setting::TypeGroup);
278
279                // we get a value stored in the "class" attribute
280                string class_name;
281                if( !link.result.lookupValue( "class", class_name ) )
282                        throw UIException( "the obligatory \"class\" identifier is missing", link.result );
283       
284                // then we find a user-info related to this type
285                const UI& related_UI = MappedUI::retrieve_ui( class_name );
286               
287                root* typeless_instance = related_UI.new_instance();
288
289                instance = dynamic_cast<T*>(typeless_instance);
290                if (!instance)
291                        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 );
292               
293                try
294                {
295                        instance->from_setting( link.result );
296                }
297                catch(SettingException xcptn)
298                {
299                        throw UIException( "the method " + class_name + ".from_setting(Setting&) has thrown an SettingException. Try to correct this method", xcptn.getPath());
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        {
306                const SettingResolver link( element );
307
308                assert_type(link.result,Setting::TypeList);
309
310                int len = link.result.getLength();
311                array_to_load.set_length( len );
312                if( len == 0 ) return;
313               
314                for( int i=0; i < len; i++ ) 
315                        from_setting( array_to_load(i), link.result[i] ); 
316        }
317
318protected:
319        //! Default constructor for internal use only, see \sa ParticularUI<T>
320        UI( const string& class_name, const type_info * const class_type_info ) 
321        {       
322                MappedUI::add_class( class_name, class_type_info, this );
323        }
324
325public: 
326
327        //! \name Initialization of classes
328        //!@{
329        //! The type T has to be a root descendant class
330
331        //! The new instance of type T* is constructed and initialized with values stored in the Setting element[name]
332        template<class T> static T* build( const Setting &element, const string &name )
333        {                       
334                T* instance;
335                from_setting<T>( instance, to_child_setting( element, name ) );
336                return instance;
337        }
338        //! The new instance of type T* is constructed and initialized with values stored in the Setting element[index]
339        template<class T> static T* build( const Setting &element, const int index )
340        {
341                T* instance;
342                from_setting<T>( instance, to_child_setting( element, index ) );
343                return instance;
344        }
345        //! The new instance of type T* is constructed and initialized with values stored in the Setting element
346        template<class T> static T* build( const Setting &element )
347        {
348                T* instance;
349                from_setting<T>( instance,  element );
350                return instance;
351        }
352        //!@}
353
354        //! \name Initialization of structures
355        //!@{
356        //! The type T has to be int, double, string, vec, ivec or mat.
357
358        //! The existing instance of type T is initialized with values stored in the Setting element[name]
359        template<class T> static void get( T &instance, const Setting &element, const string &name )
360        {
361                from_setting( instance, to_child_setting( element, name ) );
362        }
363
364        //! The existing instance of type T is initialized with values stored in the Setting element[index]
365        template<class T> static void get( T &instance, const Setting &element, const int index )
366        {
367                from_setting( instance, to_child_setting( element, index ) );
368        }
369
370        //! The existing instance of type T is initialized with values stored in the Setting element directly
371        template<class T> static void get( T &instance, const Setting &element  )
372        {
373                from_setting( instance, element );
374        }
375        //!@}
376
377        //! \name Initialization of arrays Array<T>
378        //!@{
379        //! The type T has to be int, double, string, vec, ivec or mat, or pointer to any root descendant.
380
381        //! The existing array of type T is initialized with values stored in the Setting element[name]
382        template<class T> static void get( Array<T> &array_to_load, const Setting &element, const string &name )
383        {
384                from_setting( array_to_load, to_child_setting( element, name ) );
385        }
386
387        //! The existing array of type T is initialized with values stored in the Setting element[index]
388        template<class T> static void get( Array<T> &array_to_load, const Setting &element, const int index )
389        {
390                from_setting( array_to_load, to_child_setting( element, index ) );
391        }
392
393        //! The existing array of type T is initialized with values stored in the Setting element
394        template<class T> static void get( Array<T> &array_to_load, const Setting &element  )
395        {
396                from_setting( array_to_load, element );
397        }
398        //!@}
399
400        //! \name Serialization of objects and structures into a new Setting
401        //!@{
402        //! The new child Setting can be accessed either by its name - if some name is passed as a parameter -
403        //! or by its integer index. In that case, the new element is added at the very end of the current list of child Settings.
404
405        //! A root descendant instance is stored in the new child Setting appended to the passed element
406        template< class T> static void save( const T * const instance, Setting &element, const string &name = "")
407        {
408                Setting &set = (name == "") ? element.add( Setting::TypeGroup )                                                 
409                                                                         : element.add( name, Setting::TypeGroup );             
410
411                const string &class_name = MappedUI::retrieve_class_name( &typeid(*instance) );
412                       
413                // add attribute "class"
414                Setting &type = set.add( "class", Setting::TypeString );
415                type = class_name;
416
417                try
418                {
419                        instance->to_setting( set );
420                }
421                catch(SettingException xcptn)
422                {
423                        throw UIException( "the method " + class_name + ".to_setting(Setting&) has thrown an SettingException. Try to correct this method", xcptn.getPath());
424                }       
425        }
426
427        //! An Array<T> instance is stored in the new child Setting appended to the passed element
428        template<class T> static void save( const Array<T> &array_to_save, Setting &element, const string &name = "" )
429        {
430                assert_type(element,Setting::TypeGroup);
431                Setting &list = (name == "") ? element.add( Setting::TypeList )                                                 
432                                                                         : element.add( name, Setting::TypeList );             
433                for( int i=0; i<array_to_save.length(); i++ ) 
434                        save( array_to_save(i), list );
435        }
436
437        //! A matrix(of type mat) is stored in the new child Setting appended to the passed element
438        static void save( const mat &matrix, Setting &element, const string &name = "" );
439
440        //! An integer vector (of type ivec) is stored in the new child Setting appended to the passed element
441        static void save( const ivec &vec, Setting &element, const string &name = "" );
442       
443        //! A double vector (of type vec) is stored in the new child Setting appended to the passed element
444        static void save( const vec &vector, Setting &element, const string &name = "" );
445
446        //! A string is stored in the new child Setting appended to the passed element
447        static void save( const string &str, Setting &element, const string &name = "" );
448
449        //! An integer is stored in the new child Setting appended to the passed element
450        static void save( const int &integer, Setting &element, const string &name = "" );
451
452        //! A double is stored in the new child Setting appended to the passed element
453        static void save( const double &real, Setting &element, const string &name = "" );     
454        //!@}
455
456};
457
458
459//! The only UI descendant class which is not intended for direct use. It should be accessed within the ::UIREGISTER macro only.
460template<typename T> class ParticularUI : private UI
461{
462private:
463        //! Default constructor, which is intentionally declared as private
464        ParticularUI<T>( const string &class_name) : UI( class_name, &typeid(T) ) 
465        {};
466
467public:
468        //! The only instance of this class (each type T has its own instance) which is used as a factory for processing related UI
469        static const ParticularUI<T>& factory; 
470
471        //! 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
472        root* new_instance() const
473        {
474                return new T();
475        }
476};
477
478}
479
480#endif // #ifndef USER_INFO_H
Note: See TracBrowser for help on using the browser.