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

Revision 863, 25.7 kB (checked in by smidl, 14 years ago)

Correction to new log_level -- FIXME - wrong parsing of array<string> in LOG_LEVEL

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