| 1 | // |
|---|
| 2 | // C++ Implementation: user_info.cpp |
|---|
| 3 | // |
|---|
| 4 | // Description: UI (user info) class for loading/saving objects from/to configuration files. |
|---|
| 5 | // |
|---|
| 6 | // |
|---|
| 7 | // Author: smidl <smidl@utia.cas.cz>, (C) 2009 |
|---|
| 8 | // |
|---|
| 9 | // Copyright: See COPYING file that comes with this distribution |
|---|
| 10 | // |
|---|
| 11 | // |
|---|
| 12 | |
|---|
| 13 | #include "user_info.h" |
|---|
| 14 | |
|---|
| 15 | namespace bdm { |
|---|
| 16 | |
|---|
| 17 | string UIException::format_message ( const string &reason, const string &path ) { |
|---|
| 18 | stringstream ss; |
|---|
| 19 | ss << reason; |
|---|
| 20 | ss << " Check path \"" << path << "\"."; |
|---|
| 21 | return ss.str(); |
|---|
| 22 | } |
|---|
| 23 | |
|---|
| 24 | ///////////////////////////// Class UIFile ///////////////////////////////////////////// |
|---|
| 25 | |
|---|
| 26 | UIFile::UIFile() { |
|---|
| 27 | } |
|---|
| 28 | |
|---|
| 29 | UIFile::UIFile ( const string &file_name ) { |
|---|
| 30 | try { |
|---|
| 31 | readFile ( file_name.c_str() ); |
|---|
| 32 | // this flag has to be set AFTER each file load, that is why it is right here |
|---|
| 33 | setAutoConvert ( true ); |
|---|
| 34 | } catch ( FileIOException f ) { |
|---|
| 35 | string msg = "UI error: file "; |
|---|
| 36 | msg += file_name; |
|---|
| 37 | msg += " not found."; |
|---|
| 38 | bdm_error ( msg ); |
|---|
| 39 | } catch ( ParseException& P ) { |
|---|
| 40 | stringstream msg; |
|---|
| 41 | msg << "UI error: parsing error """ << P.getError() << """ in file " << file_name << " on line " << P.getLine() << "."; |
|---|
| 42 | bdm_error ( msg.str() ); |
|---|
| 43 | } |
|---|
| 44 | } |
|---|
| 45 | |
|---|
| 46 | |
|---|
| 47 | void UIFile::save ( const string &file_name ) { |
|---|
| 48 | try { |
|---|
| 49 | writeFile ( file_name.c_str() ); |
|---|
| 50 | } catch ( FileIOException f ) { |
|---|
| 51 | string msg = "UI error: file "; |
|---|
| 52 | msg += file_name; |
|---|
| 53 | msg += " is inacessible."; |
|---|
| 54 | bdm_error ( msg ); |
|---|
| 55 | } |
|---|
| 56 | } |
|---|
| 57 | |
|---|
| 58 | UIFile::operator Setting&() { |
|---|
| 59 | return getRoot(); |
|---|
| 60 | } |
|---|
| 61 | |
|---|
| 62 | ///////////////////////////// Class UI::MappedUI ///////////////////////////////////////////// |
|---|
| 63 | |
|---|
| 64 | UI::MappedUI::StringToUIMap& UI::MappedUI::mapped_strings() { |
|---|
| 65 | // this way it is ensured that there is only one instance of StringTpUIMap, and |
|---|
| 66 | // what is more, this declaration leaves its allocation/deallocation on the compiler |
|---|
| 67 | static StringToUIMap var; |
|---|
| 68 | return var; |
|---|
| 69 | } |
|---|
| 70 | |
|---|
| 71 | UI::MappedUI::TypeInfoToStringMap& UI::MappedUI::mapped_type_infos() { |
|---|
| 72 | // this way it is ensured that there is only one instance of TypeInfoToStringMap, and |
|---|
| 73 | // what is more, this declaration leaves its allocation/deallocation on the compiler |
|---|
| 74 | static TypeInfoToStringMap var; |
|---|
| 75 | return var; |
|---|
| 76 | } |
|---|
| 77 | |
|---|
| 78 | void UI::MappedUI::add_class ( const string &class_name, const type_info * const class_type_info, const UI* const ui ) { |
|---|
| 79 | pair< StringToUIMap::iterator, bool> inres = |
|---|
| 80 | mapped_strings().insert ( |
|---|
| 81 | StringToUIMap::value_type ( class_name, ui ) ); |
|---|
| 82 | if ( inres.second ) { |
|---|
| 83 | mapped_type_infos().insert ( |
|---|
| 84 | TypeInfoToStringMap::value_type ( |
|---|
| 85 | class_type_info, class_name ) ); |
|---|
| 86 | } |
|---|
| 87 | } |
|---|
| 88 | |
|---|
| 89 | void UI::MappedUI::unregistered_class_error ( const string &unregistered_class_name ) { |
|---|
| 90 | stringstream msg; |
|---|
| 91 | msg << "UI error: class " + unregistered_class_name + " was not properly registered. Use the macro ""UIREGISTER([class name]);"" within your code." << endl; |
|---|
| 92 | |
|---|
| 93 | if ( mapped_strings().size() ) { |
|---|
| 94 | StringToUIMap::const_iterator iter = mapped_strings().begin(); |
|---|
| 95 | msg << "These classes are already registered: " << iter->first; |
|---|
| 96 | for ( iter++; iter != mapped_strings().end(); iter++ ) |
|---|
| 97 | msg << ", " << iter->first; |
|---|
| 98 | msg << "." << endl; |
|---|
| 99 | } else |
|---|
| 100 | msg << "There is not any registered class yet!" << endl; |
|---|
| 101 | |
|---|
| 102 | bdm_error ( msg.str() ); |
|---|
| 103 | } |
|---|
| 104 | |
|---|
| 105 | const UI& UI::MappedUI::retrieve_ui ( const string &class_name ) { |
|---|
| 106 | StringToUIMap::const_iterator iter = mapped_strings().find ( class_name ); |
|---|
| 107 | if ( iter == mapped_strings().end() ) |
|---|
| 108 | unregistered_class_error ( class_name ); |
|---|
| 109 | |
|---|
| 110 | return *iter->second; |
|---|
| 111 | } |
|---|
| 112 | |
|---|
| 113 | const string& UI::MappedUI::retrieve_class_name ( const type_info * const class_type_info ) { |
|---|
| 114 | TypeInfoToStringMap::const_iterator iter = mapped_type_infos().find ( class_type_info ); |
|---|
| 115 | if ( iter == mapped_type_infos().end() ) |
|---|
| 116 | unregistered_class_error ( "with RTTI name " + string ( class_type_info->name() ) ); |
|---|
| 117 | return iter->second; |
|---|
| 118 | } |
|---|
| 119 | |
|---|
| 120 | ///////////////////////////// Class SettingResolver ///////////////////////////////////////////// |
|---|
| 121 | |
|---|
| 122 | SettingResolver::SettingResolver ( const Setting &potential_link ) |
|---|
| 123 | : result ( initialize_reference ( file, potential_link ) ) { |
|---|
| 124 | } |
|---|
| 125 | |
|---|
| 126 | const Setting& SettingResolver::initialize_reference ( UIFile *&file, const Setting &potential_link ) { |
|---|
| 127 | file = NULL; |
|---|
| 128 | |
|---|
| 129 | if ( potential_link.getType() != Setting::TypeString ) |
|---|
| 130 | return potential_link; |
|---|
| 131 | |
|---|
| 132 | string link = ( const char* ) potential_link; |
|---|
| 133 | size_t aerobase = link.find ( '@' ); |
|---|
| 134 | |
|---|
| 135 | const Setting *result; |
|---|
| 136 | if ( aerobase != string::npos ) { |
|---|
| 137 | string file_name = link.substr ( aerobase + 1, link.length() ); |
|---|
| 138 | file = new UIFile ( file_name ); |
|---|
| 139 | result = & ( Setting& ) ( *file ); |
|---|
| 140 | link = link.substr ( 0, aerobase ); |
|---|
| 141 | } else { |
|---|
| 142 | result = &potential_link; |
|---|
| 143 | while ( !result->isRoot() ) |
|---|
| 144 | result = &result->getParent(); |
|---|
| 145 | } |
|---|
| 146 | |
|---|
| 147 | if ( !result->exists ( link ) ) |
|---|
| 148 | throw UISettingException ( "UIException: linked setting was not found.", string ( ( const char* ) potential_link ) ); |
|---|
| 149 | |
|---|
| 150 | return ( *result ) [link]; |
|---|
| 151 | } |
|---|
| 152 | |
|---|
| 153 | SettingResolver::~SettingResolver() { |
|---|
| 154 | if ( file ) delete file; |
|---|
| 155 | } |
|---|
| 156 | |
|---|
| 157 | ///////////////////////////// Class UI ///////////////////////////////////////////// |
|---|
| 158 | |
|---|
| 159 | void UI::assert_type ( const Setting &element, Setting::Type type ) { |
|---|
| 160 | if ( element.getType() != type ) |
|---|
| 161 | throw UISettingException ( "UIException: wrong setting type.", element ); |
|---|
| 162 | } |
|---|
| 163 | |
|---|
| 164 | const Setting& UI::to_child_setting ( const Setting &element, const int index ) { |
|---|
| 165 | if ( !element.isList() ) |
|---|
| 166 | throw UISettingException ( "UIException: only TypeList elements could be indexed by integers.", element ); |
|---|
| 167 | |
|---|
| 168 | return element[index]; |
|---|
| 169 | } |
|---|
| 170 | |
|---|
| 171 | const Setting& UI::to_child_setting ( const Setting &element, const string &name ) { |
|---|
| 172 | if ( !element.isGroup() ) |
|---|
| 173 | throw UISettingException ( "UIException: only TypeGroup elements could be indexed by strings.", element ); |
|---|
| 174 | |
|---|
| 175 | return element[name]; |
|---|
| 176 | } |
|---|
| 177 | |
|---|
| 178 | void UI::call_to_setting( const root &instance, Setting &set, string class_name ) { |
|---|
| 179 | try { |
|---|
| 180 | instance.to_setting ( set ); |
|---|
| 181 | } catch ( SettingException &sttng_xcptn ) { |
|---|
| 182 | string msg = "UIException: method "; |
|---|
| 183 | msg += class_name; |
|---|
| 184 | msg += ".to_setting(Setting&) has thrown a SettingException."; |
|---|
| 185 | throw UISettingException ( msg, sttng_xcptn.getPath() ); |
|---|
| 186 | } |
|---|
| 187 | } |
|---|
| 188 | |
|---|
| 189 | void UI::save ( const root &instance, Setting &element, const string &name ) { |
|---|
| 190 | Setting &set = ( name == "" ) ? (element.getType()==Setting::TypeArray || element.getType()==Setting::TypeList) ? element.add ( Setting::TypeGroup ) : element |
|---|
| 191 | : element.add ( name, Setting::TypeGroup ); |
|---|
| 192 | |
|---|
| 193 | call_to_setting( instance, set ); |
|---|
| 194 | } |
|---|
| 195 | |
|---|
| 196 | void UI::save ( const log_level_base &log_level, Setting &element ) { |
|---|
| 197 | assert_type ( element, Setting::TypeGroup ); |
|---|
| 198 | string name = "log_level"; |
|---|
| 199 | |
|---|
| 200 | if( element.exists( name ) ) |
|---|
| 201 | assert_type ( element[name], Setting::TypeString ); |
|---|
| 202 | else |
|---|
| 203 | element.add ( name, Setting::TypeString ); |
|---|
| 204 | |
|---|
| 205 | call_to_setting( log_level, element[name] ); |
|---|
| 206 | } |
|---|
| 207 | |
|---|
| 208 | void UI::save ( const root * const instance, Setting &element, const string &name ) { |
|---|
| 209 | Setting &set = ( name == "" ) ? (element.getType()==Setting::TypeArray || element.getType()==Setting::TypeList) ? element.add ( Setting::TypeGroup ) : element |
|---|
| 210 | : element.add ( name, Setting::TypeGroup ); |
|---|
| 211 | |
|---|
| 212 | // add attribute "class" |
|---|
| 213 | const string &class_name = MappedUI::retrieve_class_name ( &typeid ( *instance ) ); |
|---|
| 214 | Setting &type = set.add ( "class", Setting::TypeString ); |
|---|
| 215 | type = class_name; |
|---|
| 216 | |
|---|
| 217 | call_to_setting( *instance, set, class_name ); |
|---|
| 218 | } |
|---|
| 219 | |
|---|
| 220 | void UI::save ( const int &integer, Setting &element, const string &name ) { |
|---|
| 221 | Setting &set = ( name == "" ) ? element.add ( Setting::TypeInt ) |
|---|
| 222 | : element.add ( name, Setting::TypeInt ); |
|---|
| 223 | set = integer; |
|---|
| 224 | } |
|---|
| 225 | |
|---|
| 226 | void UI::save ( const double &real, Setting &element, const string &name ) { |
|---|
| 227 | Setting &set = ( name == "" ) ? element.add ( Setting::TypeFloat ) |
|---|
| 228 | : element.add ( name, Setting::TypeFloat ); |
|---|
| 229 | set = real; |
|---|
| 230 | } |
|---|
| 231 | |
|---|
| 232 | void UI::save ( const string &str, Setting &element, const string &name ) { |
|---|
| 233 | Setting &set = ( name == "" ) ? element.add ( Setting::TypeString ) |
|---|
| 234 | : element.add ( name, Setting::TypeString ); |
|---|
| 235 | set = str; |
|---|
| 236 | } |
|---|
| 237 | |
|---|
| 238 | void UI::save ( const mat &matrix, Setting &element, const string &name ) { |
|---|
| 239 | Setting &set = ( name == "" ) ? element.add ( Setting::TypeList ) |
|---|
| 240 | : element.add ( name, Setting::TypeList ); |
|---|
| 241 | |
|---|
| 242 | Setting &tag = set.add ( Setting::TypeString ); |
|---|
| 243 | tag = "matrix"; |
|---|
| 244 | |
|---|
| 245 | Setting &rows = set.add ( Setting::TypeInt ); |
|---|
| 246 | rows = matrix.rows(); |
|---|
| 247 | |
|---|
| 248 | Setting &cols = set.add ( Setting::TypeInt ); |
|---|
| 249 | cols = matrix.cols(); |
|---|
| 250 | |
|---|
| 251 | Setting &elements = set.add ( Setting::TypeArray ); |
|---|
| 252 | |
|---|
| 253 | // build matrix row-wise |
|---|
| 254 | for ( int i = 0; i < matrix.rows(); i++ ) |
|---|
| 255 | for ( int j = 0; j < matrix.cols(); j++ ) { |
|---|
| 256 | Setting &new_field = elements.add ( Setting::TypeFloat ); |
|---|
| 257 | new_field = matrix ( i, j ); |
|---|
| 258 | } |
|---|
| 259 | } |
|---|
| 260 | |
|---|
| 261 | void UI::save ( const ivec &vector, Setting &element, const string &name ) { |
|---|
| 262 | Setting &set = ( name == "" ) ? element.add ( Setting::TypeArray ) |
|---|
| 263 | : element.add ( name, Setting::TypeArray ); |
|---|
| 264 | for ( int i = 0; i < vector.length(); i++ ) { |
|---|
| 265 | Setting &new_field = set.add ( Setting::TypeInt ); |
|---|
| 266 | new_field = vector ( i ); |
|---|
| 267 | } |
|---|
| 268 | } |
|---|
| 269 | |
|---|
| 270 | void UI::save ( const vec &vector, Setting &element, const string &name ) { |
|---|
| 271 | Setting &set = ( name == "" ) ? element.add ( Setting::TypeArray ) |
|---|
| 272 | : element.add ( name, Setting::TypeArray ); |
|---|
| 273 | for ( int i = 0; i < vector.length(); i++ ) { |
|---|
| 274 | Setting &new_field = set.add ( Setting::TypeFloat ); |
|---|
| 275 | new_field = vector ( i ); |
|---|
| 276 | } |
|---|
| 277 | } |
|---|
| 278 | |
|---|
| 279 | |
|---|
| 280 | void UI::call_from_setting( root &instance, const Setting &set, string class_name) { |
|---|
| 281 | try { |
|---|
| 282 | instance.from_setting ( set ); |
|---|
| 283 | } catch ( SettingException &sttng_xcptn ) { |
|---|
| 284 | string msg = "UIException: method "; |
|---|
| 285 | msg += class_name; |
|---|
| 286 | msg += ".from_setting(Setting&) has thrown a SettingException."; |
|---|
| 287 | throw UISettingException ( msg, sttng_xcptn.getPath() ); |
|---|
| 288 | } catch ( std::runtime_error &e ) { |
|---|
| 289 | string msg = "UIException: method "; |
|---|
| 290 | msg += class_name; |
|---|
| 291 | msg += " says: "; |
|---|
| 292 | msg += e.what(); |
|---|
| 293 | throw UISettingException ( msg, set ); |
|---|
| 294 | } |
|---|
| 295 | |
|---|
| 296 | // validate the new instance |
|---|
| 297 | instance.validate(); |
|---|
| 298 | } |
|---|
| 299 | |
|---|
| 300 | void UI::from_setting ( log_level_base &log_level, const Setting &element ) { |
|---|
| 301 | assert_type( element, Setting::TypeString ); |
|---|
| 302 | call_from_setting( log_level, element ); |
|---|
| 303 | } |
|---|
| 304 | |
|---|
| 305 | void UI::from_setting ( root &instance, const Setting &element ) { |
|---|
| 306 | const SettingResolver link ( element ); |
|---|
| 307 | assert_type( link.result, Setting::TypeGroup ); |
|---|
| 308 | call_from_setting( instance, link.result); |
|---|
| 309 | } |
|---|
| 310 | |
|---|
| 311 | void UI::from_setting ( mat& matrix, const Setting &element ) { |
|---|
| 312 | const SettingResolver link ( element ); |
|---|
| 313 | |
|---|
| 314 | if ( link.result.isNumber() ) { |
|---|
| 315 | matrix.set_size ( 1, 1 ); |
|---|
| 316 | matrix ( 0, 0 ) = link.result; |
|---|
| 317 | return; |
|---|
| 318 | } |
|---|
| 319 | |
|---|
| 320 | if ( link.result.isList() ) { |
|---|
| 321 | int data_offset; |
|---|
| 322 | |
|---|
| 323 | if ( link.result.getLength() == 3 ) |
|---|
| 324 | data_offset = 0; |
|---|
| 325 | else if ( link.result.getLength() == 4 ) { |
|---|
| 326 | assert_type ( link.result[0], Setting::TypeString ); |
|---|
| 327 | const char* elem1 = ( const char* ) link.result[0]; |
|---|
| 328 | if ( ( strcmp ( elem1, "matrix" ) ) ) |
|---|
| 329 | throw UISettingException ( "UIException: the setting supposed to represent a matrix element has wrong syntax.", link.result ); |
|---|
| 330 | |
|---|
| 331 | data_offset = 1; |
|---|
| 332 | } else |
|---|
| 333 | throw UISettingException ( "UIException: the setting supposed to represent a matrix element has wrong syntax.", link.result ); |
|---|
| 334 | |
|---|
| 335 | Setting &rows_setting = link.result[0 + data_offset]; |
|---|
| 336 | Setting &cols_setting = link.result[1 + data_offset]; |
|---|
| 337 | Setting &elements = link.result[2 + data_offset]; |
|---|
| 338 | |
|---|
| 339 | // vvv ----- not working in matlab!! |
|---|
| 340 | //assert_type ( cols_setting, Setting::TypeInt ); |
|---|
| 341 | //assert_type ( rows_setting, Setting::TypeInt ); |
|---|
| 342 | //assert_type ( elements, Setting::TypeArray ); |
|---|
| 343 | |
|---|
| 344 | int cols = cols_setting; |
|---|
| 345 | int rows = rows_setting; |
|---|
| 346 | int elems = elements.getLength(); |
|---|
| 347 | |
|---|
| 348 | if ( cols < 0 || rows < 0 ) |
|---|
| 349 | throw UISettingException ( "UIException: the dimensions of a matrix has to be non-negative.", link.result ); |
|---|
| 350 | |
|---|
| 351 | if ( elems != cols * rows ) |
|---|
| 352 | throw UISettingException ( "UIException: the count of the matrix elements is incompatible with matrix dimension.", elements ); |
|---|
| 353 | |
|---|
| 354 | matrix.set_size ( rows, cols ); |
|---|
| 355 | |
|---|
| 356 | if ( cols == 0 || rows == 0 ) |
|---|
| 357 | return; |
|---|
| 358 | |
|---|
| 359 | if ( !elements[0].isNumber() ) |
|---|
| 360 | throw UISettingException ( "UIException: matrix elements have to be numbers.", elements[0] ); |
|---|
| 361 | |
|---|
| 362 | // build matrix row-wise |
|---|
| 363 | int k = 0; |
|---|
| 364 | for ( int i = 0; i < rows; i++ ) |
|---|
| 365 | for ( int j = 0; j < cols; j++ ) |
|---|
| 366 | matrix ( i, j ) = elements[k++]; |
|---|
| 367 | return; |
|---|
| 368 | } |
|---|
| 369 | |
|---|
| 370 | throw UISettingException ( "UIException: only numeric types or TypeList are supported as matrix values.", link.result ); |
|---|
| 371 | } |
|---|
| 372 | |
|---|
| 373 | void UI::from_setting ( vec &vector, const Setting &element ) { |
|---|
| 374 | const SettingResolver link ( element ); |
|---|
| 375 | |
|---|
| 376 | if ( link.result.isNumber() ) { |
|---|
| 377 | vector.set_length ( 1 ); |
|---|
| 378 | vector ( 0 ) = link.result; |
|---|
| 379 | return; |
|---|
| 380 | } |
|---|
| 381 | |
|---|
| 382 | if ( link.result.isList() ) { |
|---|
| 383 | mat matrix; |
|---|
| 384 | from_setting ( matrix, link.result ); |
|---|
| 385 | |
|---|
| 386 | if ( matrix.cols() != 1 && matrix.rows() != 1 && matrix.cols() != 0 ) |
|---|
| 387 | throw UISettingException ( "UIException: the vector length is invalid, it seems to be rather a matrix.", link.result ); |
|---|
| 388 | |
|---|
| 389 | int len = matrix.rows() * matrix.cols(); |
|---|
| 390 | vector.set_length ( len ); |
|---|
| 391 | if ( len == 0 ) return; |
|---|
| 392 | |
|---|
| 393 | if ( matrix.cols() == 1 ) |
|---|
| 394 | for ( int i = 0; i < len; i++ ) |
|---|
| 395 | vector ( i ) = matrix ( i, 0 ); |
|---|
| 396 | else |
|---|
| 397 | for ( int i = 0; i < len; i++ ) |
|---|
| 398 | vector ( i ) = matrix ( 0, i ); |
|---|
| 399 | return; |
|---|
| 400 | } |
|---|
| 401 | |
|---|
| 402 | if ( link.result.isArray() ) { |
|---|
| 403 | int len = link.result.getLength(); |
|---|
| 404 | vector.set_length ( len ); |
|---|
| 405 | if ( len == 0 ) return; |
|---|
| 406 | |
|---|
| 407 | if ( !link.result[0].isNumber() ) |
|---|
| 408 | throw UISettingException ( "UIException: a vector element has to be a number.", link.result[0] ); |
|---|
| 409 | |
|---|
| 410 | for ( int i = 0; i < len; i++ ) |
|---|
| 411 | vector ( i ) = link.result[i]; |
|---|
| 412 | |
|---|
| 413 | return; |
|---|
| 414 | } |
|---|
| 415 | |
|---|
| 416 | throw UISettingException ( "UIException: only numeric types, TypeArray or TypeList are supported as vector values.", link.result ); |
|---|
| 417 | } |
|---|
| 418 | |
|---|
| 419 | void UI::from_setting ( ivec &vector, const Setting &element ) { |
|---|
| 420 | vec double_vector; |
|---|
| 421 | from_setting ( double_vector, element ); |
|---|
| 422 | int len = double_vector.length(); |
|---|
| 423 | vector.set_length ( len ); |
|---|
| 424 | for ( int i = 0; i < len; i++ ) |
|---|
| 425 | vector ( i ) = ( int ) double_vector ( i ); |
|---|
| 426 | } |
|---|
| 427 | |
|---|
| 428 | void UI::from_setting ( string &str, const Setting &element ) { |
|---|
| 429 | assert_type ( element, Setting::TypeString ); |
|---|
| 430 | str = ( const char* ) element; |
|---|
| 431 | } |
|---|
| 432 | |
|---|
| 433 | void UI::from_setting ( int &integer, const Setting &element ) { |
|---|
| 434 | // assert_type ( element, Setting::TypeInt ); |
|---|
| 435 | integer = element; |
|---|
| 436 | } |
|---|
| 437 | |
|---|
| 438 | void UI::from_setting ( double &real, const Setting &element ) { |
|---|
| 439 | // assert_type ( element, Setting::TypeFloat ); |
|---|
| 440 | real = element; |
|---|
| 441 | } |
|---|
| 442 | |
|---|
| 443 | }//namespace |
|---|