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

Revision 396, 17.4 kB (checked in by mido, 15 years ago)

UI documentation almost finished; UIException redesigned and used instead of UI::ui_error function

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