1 /* _________________________________________________________________________ 2 * 3 * UTILIB: A utility library for developing portable C++ codes. 4 * Copyright (c) 2008 Sandia Corporation. 5 * This software is distributed under the BSD License. 6 * Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, 7 * the U.S. Government retains certain rights in this software. 8 * For more information, see the README file in the top UTILIB directory. 9 * _________________________________________________________________________ 10 */ 11 12 /** 13 * \file Property.h 14 * 15 * Defines the utilib::Property, utilib::ReadOnly_Property, and 16 * utilib::Privileged_Property classes. 17 */ 18 19 #ifndef utilib_Property_h 20 #define utilib_Property_h 21 22 #include <utilib/std_headers.h> 23 #include <utilib/ReferenceCounted.h> 24 #include <utilib/Any.h> 25 #include <utilib/TypeManager.h> 26 27 #ifdef UTILIB_HAVE_BOOST 28 #include <boost/function.hpp> 29 #include <boost/signals2.hpp> 30 #endif // UTILIB_HAVE_BOOST 31 32 namespace utilib { 33 34 class property_error : public std::runtime_error 35 { 36 public: 37 /// Constructor property_error(const std::string & msg)38 property_error(const std::string& msg) 39 : runtime_error(msg) 40 {} 41 }; 42 43 44 class Property; 45 class Privileged_Property; 46 47 /** The utilib::ReadOnly_Property class implements the bulk of the 48 * utilib::Property class. This provides all of the functionality of 49 * the Property class, except for the ability to set new property 50 * values. 51 * 52 * See the utilib::Property documentation for a discussion of the API. 53 */ 54 class ReadOnly_Property 55 { 56 #ifdef UTILIB_HAVE_BOOST 57 struct logical_and_combiner 58 { 59 typedef bool result_type; 60 61 template<typename InputIterator> operatorlogical_and_combiner62 bool operator()(InputIterator first, InputIterator last) const 63 { 64 if (first == last) 65 return true; 66 67 bool ans = true; 68 for( ; first != last; ++first ) 69 ans &= *first; 70 return ans; 71 } 72 }; 73 74 public: 75 // NB: the "&" for the arguments IS REQUIRED for things to work 76 // correctly, and many compilers WILL NOT catch it if you omit it. 77 // [get & set function parameters are void(prop_data, external_value)] 78 typedef boost::function<void( utilib::Any&, const utilib::Any )> set_fcn_t; 79 typedef boost::function<void( const utilib::Any&, utilib::Any& )> get_fcn_t; 80 typedef boost::signals2::signal< void( const ReadOnly_Property& ) > onChange_t; 81 typedef boost::signals2::signal< bool( const ReadOnly_Property&, const Any& ), 82 logical_and_combiner > validate_t; 83 connected(set_fcn_t & f)84 inline bool connected(set_fcn_t &f) const { return ! f.empty(); } connected(get_fcn_t & f)85 inline bool connected(get_fcn_t &f) const { return ! f.empty(); } 86 #else 87 /// Simple proxy for the boost::signals class 88 template<typename FCN> 89 class Listener 90 { 91 public: 92 void connect( FCN cb ) 93 { listeners.insert(cb); } 94 95 void disconnect( FCN cb ) 96 { listeners.erase(cb); } 97 98 protected: 99 std::set<FCN> listeners; 100 typedef typename std::set<FCN>::iterator iterator_t; 101 }; 102 103 /// Specialization for the onChange() callback 104 class OnChangeListener : public Listener<void(*)(const ReadOnly_Property&)> 105 { 106 public: 107 void operator()(const ReadOnly_Property& property) 108 { 109 iterator_t itEnd = listeners.end(); 110 for(iterator_t it = listeners.begin(); it != itEnd; ++it) 111 (*it)(property); 112 } 113 }; 114 115 /// Specialization for the validate() callback 116 class ValidateListener 117 : public Listener<bool(*)(const ReadOnly_Property&, const Any&)> 118 { 119 public: 120 bool operator()(const ReadOnly_Property& property, Any& new_value) 121 { 122 bool ans = true; 123 iterator_t itEnd = listeners.end(); 124 for(iterator_t it = listeners.begin(); it != itEnd; ++it) 125 ans &= (*it)(property, new_value); 126 return ans; 127 } 128 }; 129 130 public: 131 // NB: the "&" for the arguments IS REQUIRED for things to work 132 // correctly, and many compilers WILL NOT catch it if you omit it. 133 // [get & set function parameters are void(prop_data, external_value)] 134 typedef void(*set_fcn_t)(utilib::Any&, const utilib::Any); 135 typedef void(*get_fcn_t)(const utilib::Any&, utilib::Any&); 136 typedef OnChangeListener onChange_t; 137 typedef ValidateListener validate_t; 138 139 inline bool connected(set_fcn_t &f) const { return f != NULL; } 140 inline bool connected(get_fcn_t &f) const { return f != NULL; } 141 #endif // UTILIB_HAVE_BOOST 142 143 typedef void(onChange_t::*onChange_signal_t)( const ReadOnly_Property& ); 144 145 public: ReadOnly_Property()146 ReadOnly_Property() 147 : data() 148 { 149 data->value.set<UntypedAnyContainer>(); 150 data->set_functor = set_fcn_t(); 151 data->get_functor = get_fcn_t(); 152 } 153 ReadOnly_Property(const ReadOnly_Property & rhs)154 ReadOnly_Property(const ReadOnly_Property& rhs) 155 : data(rhs.data) 156 {} 157 ~ReadOnly_Property()158 virtual ~ReadOnly_Property() 159 {} 160 161 /// Get the current property value get()162 inline utilib::Any get() const 163 { 164 if ( connected(data->get_functor) ) 165 { 166 utilib::Any tmp; 167 data->get_functor(data->value, tmp); 168 169 // We want to defeat the normal Any shallow copies so that the 170 // user can't set the ReadOnly_Property value without going 171 // through the set() method. 172 if ( tmp.anyCount() == 0 ) 173 return tmp; 174 else 175 return tmp.clone(); 176 } 177 else if ( data->value.is_type(typeid(UntypedAnyContainer)) ) 178 return data->value.expose<UntypedAnyContainer>().m_data.clone(); 179 else 180 return data->value.clone(); 181 } 182 183 /// Rebind the get and set functors to call new functions 184 /* JDS: Disabled until we determine it is really needed. 185 */ 186 //void rebind(set_fcn_t set = set_fcn_t(), get_fcn_t get = get_fcn_t()) 187 //{ 188 // set_functor = set; 189 // get_functor = get; 190 //} 191 192 /// Convenience method for implicitly getting the value of a property 193 template<typename T> T()194 operator T() const 195 { 196 return as<T>(); 197 } 198 199 /// Convenience method for explicitly getting the value of a property 200 template<typename T> as()201 T as() const 202 { 203 utilib::Any tmp; 204 TypeManager()->lexical_cast(get(), tmp, typeid(T), false); 205 return tmp.template expose<T>(); 206 } 207 208 /// Convenience method for getting a reference to the stored data 209 /** This method somewhat violates the spirit of the Property class, 210 * in that it allows the user to obtain a reference to the bound, 211 * contained data. In theory, providing this method can allow users 212 * to directly change the contained value (via const_cast), 213 * bypassing the validate() and onChange() events. 214 * 215 * However, for accessing elements of properties that contain large 216 * amounts of data (i.e. an element from a vector), this is 217 * significantly more efficient (it bypasses several functions and a 218 * complete copy of the data). 219 */ 220 template<typename T> expose()221 const T& expose() const 222 { 223 if ( connected(data->get_functor) ) 224 EXCEPTION_MNGR(property_error, "Property::expose(): not availiable " 225 "for Properties with non-default get_functor"); 226 227 return data->value.expose<T>(); 228 } 229 asAnyRef()230 virtual utilib::AnyRef asAnyRef() const 231 { return utilib::AnyRef(*this); } 232 asAnyFixedRef()233 virtual utilib::AnyFixedRef asAnyFixedRef() const 234 { return utilib::AnyFixedRef(*this); } 235 236 /// Special case for getting the value of the property into an Any 237 /** This explicit conversion is needed to resolve an ambiguity 238 * between using the Property's templated conversion operator 239 * [operator T() const] and the templated utilib::Any constructor. 240 * By providing an explicit conversion operator, the compiler will 241 * pick this function over the templated Any constructor. 242 */ Any()243 operator utilib::Any() const 244 { return get(); } 245 246 // For most compilers, these explicit conversion operations are 247 // legitimate. However, for AIX compilers (xlC v8.0, tested Feb 2010), 248 // the compiler throws an ambiguous override error for these explicit 249 // conversion operations when attempting: 250 // utilib::Property p(utilib::Property::Bind<int>(5)); 251 // utilib::Any a = p; 252 // 253 // For that platform, conversions to AnyRef and AnyFixedRef MUST either use: 254 // utilib::AnyRef a1 = p.as<utilib::AnyRef>(); 255 // utilib::AnyFixedRef a2 = p.as<utilib::AnyFixedRef>(); 256 // or the explicit Any::set() method. 257 // 258 #if ! defined(UTILIB_AIX_CC) 259 260 /// Special case for getting an AnyRef that holds this property 261 /** This prevents the ambiguous case of should I convert the Property 262 * to a "AnyRef" using the Property's implicit cast operator, or 263 * should I use the AnyRef's implicit conctructor? That is: 264 * \code 265 * utilib::Property p; 266 * utilib::AnyRef a = p; 267 * \endcode 268 * 269 * This forces the latter case. 270 * 271 * - GCC (by chance) picks up the latter automatically. 272 * - ICC threw up its arms in confusion. 273 */ AnyRef()274 operator utilib::AnyRef() const 275 { return asAnyRef(); } 276 277 /// Special case for getting an AnyFixedRef that holds this property 278 /** This prevents the ambiguous case of should I convert the Property 279 * to a "AnyFixedRef" using the Property's implicit cast operator, or 280 * should I use the AnyFixedRef's implicit conctructor? That is: 281 * \code 282 * utilib::Property p; 283 * utilib::AnyFixedRef a = p; 284 * \endcode 285 * 286 * This forces the latter case. 287 * 288 * - GCC (by chance) picks up the latter automatically. 289 * - ICC threw up its arms in confusion. 290 */ AnyFixedRef()291 operator utilib::AnyFixedRef() const 292 { return asAnyFixedRef(); } 293 294 #endif // ! defined(UTILIB_AIX_CC) 295 296 /// is this really a read-only property? readonly()297 bool readonly() const 298 { return ! data->is_writable; } 299 300 /// Convenience: implicitly compare this Property's value with "anything" 301 template<typename T> 302 bool operator==(const T& rhs) const 303 { 304 utilib::Any L = get(); 305 if ( L.empty() ) 306 return false; 307 308 utilib::Any tmp; 309 try { 310 int ans = TypeManager()->lexical_cast(L, tmp, typeid(T), false); 311 if ( ans == 0 ) 312 return tmp.template expose<T>() == rhs; catch(utilib::bad_lexical_cast & e)313 } catch ( utilib::bad_lexical_cast &e ) { 314 } 315 316 return equality_compare_any(utilib::Any(rhs)); 317 } 318 319 /// Convenience: implicitly compare this Property's value with "anything" 320 template<typename T> 321 bool operator!=(const T& rhs) const 322 { return ! operator==(rhs); } 323 324 /// Convenience: implicitly compare this Property's value with "anything" 325 template<typename T> 326 bool operator<(const T& rhs) const 327 { 328 utilib::Any L = get(); 329 if ( L.empty() ) 330 return true; 331 332 utilib::Any tmp; 333 try { 334 int ans = TypeManager()->lexical_cast(L, tmp, typeid(T), false); 335 if ( ans == 0 ) 336 return tmp.template expose<T>() < rhs; catch(utilib::bad_lexical_cast & e)337 } catch ( utilib::bad_lexical_cast &e ) { 338 } 339 340 return lessThan_compare_any(utilib::Any(rhs)); 341 } 342 343 /// Convenience: implicitly compare this Property's value with "anything" 344 template<typename T> 345 bool operator<=(const T& rhs) const 346 { return operator<(rhs) || operator==(rhs); } 347 348 /// Convenience: implicitly compare this Property's value with "anything" 349 template<typename T> 350 bool operator>(const T& rhs) const 351 { return !operator<(rhs) && !operator==(rhs); } 352 353 /// Convenience: implicitly compare this Property's value with "anything" 354 template<typename T> 355 bool operator>=(const T& rhs) const 356 { return ! operator<(rhs); } 357 358 // Convenience: implicitly convert to bool and return negation 359 bool operator!() const 360 { return ! as<bool>(); } 361 362 // Convenience: logical AND 363 bool operator&&(const bool& rhs) const 364 { return as<bool>() && rhs; } 365 366 // Convenience: logical OR 367 bool operator||(const bool& rhs) const 368 { return as<bool>() || rhs; } 369 370 /// Allow listeners to validate a new value before it is set() validate()371 validate_t & validate() const 372 { return data->validate; } 373 374 /// Allow listeners to be notified (but not validate) when the value changes onChange()375 onChange_t & onChange() const 376 { return data->onChange; } 377 378 /// True if this Property references the same data as the parameter equivalentTo(const ReadOnly_Property & ref)379 bool equivalentTo(const ReadOnly_Property& ref) const 380 { return data == ref.data; } 381 382 private: // member functions 383 384 /// Compare the value of this Property to another Property 385 bool equality_compare_property(const ReadOnly_Property& rhs) const; 386 387 /// Compare the value of this Property to the data contained in an Any 388 bool equality_compare_any(const Any& rhs) const; 389 390 /// Compare the value of this Property to another Property 391 bool lessThan_compare_property(const ReadOnly_Property& rhs) const; 392 393 /// Compare the value of this Property to the data contained in an Any 394 bool lessThan_compare_any(const Any& rhs) const; 395 396 397 protected: // member data 398 399 /// Create a new ReadOnly_Property ReadOnly_Property(Any _value,set_fcn_t set,get_fcn_t get)400 ReadOnly_Property( Any _value, set_fcn_t set, get_fcn_t get ) 401 : data() 402 { 403 data->value = _value; 404 data->set_functor = set; 405 data->get_functor = get; 406 } 407 408 /// The Data structure holds all data used by the class 409 /** This indirection structure is a convenient place to put 410 * callbacks. The ReadOnly_Property class holds the Data structure 411 * within a ReferenceCounted object. This allows us to use 412 * non-copyable callbacks (i.e. boost::signal), while still allowing 413 * the ReadOnly_Property to be "copied" around. It is also slightly more 414 * memory efficient to put all the callbacks into a single 415 * ReferenceCounted object instead of putting each into its own 416 * ReferenceCounted object. 417 */ 418 struct Data { DataData419 Data() 420 : is_writable(true) 421 {} 422 423 /// The actual data held (or referened) by this Property 424 utilib::Any value; 425 426 /// True if this Property is writable (i.e. not ReadOnly) 427 /** Although the presence of a "writable" state in addition to the 428 * ReadOnly_Property class appears to be redundant, it is 429 * necessary in order to allow insertion of read only Properties 430 * into a PropertyDict (which only holds and returns Property 431 * instances). 432 * 433 * Control over this state is implemented in the 434 * Privileged_Property class. 435 */ 436 bool is_writable; 437 438 /// Single function called to set the ReadOnly_Property value 439 set_fcn_t set_functor; 440 441 /// Single function called to get the ReadOnly_Property value 442 get_fcn_t get_functor; 443 444 /// (External) function(s) to call after the value is set() 445 onChange_t onChange; 446 /// (External) function(s) to call before the value is set() 447 validate_t validate; 448 }; 449 450 /// All data necessary for implementing the entire Property system 451 /// (including full Property and Privileged_Property classes). 452 utilib::ReferenceCounted<Data> data; 453 }; 454 455 456 /** The utilib::Property class provides a mechanism for managing 457 * "untyped" references to object "properties". The Property system 458 * makes heavy use of utilib::Any and the global utilib::TypeManager() 459 * to provide a type-agnostic interface to the property value. 460 * 461 * Key features: 462 * - Type agnostic system for accessing the property value. This 463 * relies on the TypeManager() to implicitly convert data to and from 464 * the bound property type. For example, the following is legal: 465 * 466 * \code 467 * int value = 5; 468 * utilib::Property prop(value); // bind the property to 'value' 469 * prop = 10; // 'value' now == 10 470 * int i = prop; // 'i' == 10 471 * double d = prop; // 'd' == 10.0 472 * \endcode 473 * 474 * - A Property may be bound to a variable, in which case accessing 475 * the property (read or write) reads or changes the bound variable. 476 * Properties may also be bound to a type (with the value stored in 477 * an Any within the Property), or completely unbound, where the 478 * contained data type matches the last value assigned to the 479 * Property. 480 * 481 * - Users may declare custom callbacks that are called for both 482 * getting and setting the property value. 483 * 484 * - (External) users may register callbacks to be notified when the 485 * property value has changed [see onChange()] 486 * 487 * - (External) users may register callbacks to be notified before 488 * setting a new property value, and have the opportunity to reject 489 * the new value (throwing an exception) [see validate()] 490 */ 491 class Property : public ReadOnly_Property 492 { 493 public: 494 /// Utility for binding a Property to a type (access via Property::Bind()) 495 struct BoundType { 496 private: 497 friend class Property; BoundTypeBoundType498 BoundType() : handle() {} 499 utilib::Any handle; 500 }; 501 502 /// Create an unbound property 503 /** This creates an unbound property that is not bound to a specific 504 * external data source or data type. Any assignment to an Unbound 505 * Untyped property will throw away any current value and replace it 506 * with the new value (and the new value's type). 507 * 508 * NB: This relies on the default constructor for the set_fcn_t and 509 * get_fcn_t(). If we are compiling with Boost, this will create an 510 * empty() function object. If we are compiling without Boost, per 511 * the C++ standard, the default constructor for pointer types is a 512 * NULL pointer (get() / set() relies on this). 513 */ Property()514 Property() 515 : ReadOnly_Property(UntypedAnyContainer(), set_fcn_t(), get_fcn_t()) 516 {} 517 518 /// Explicit copy constructor (to prevent accidental wrapping) Property(const Property & rhs)519 Property(const Property& rhs) 520 : ReadOnly_Property(rhs) 521 {} 522 523 /// Create an unbound property 524 /** This creates an unbound property that is not bound to a specific 525 * external data source or data type. Any assignment to an Unbound 526 * Untyped property will throw away any current value and replace it 527 * with the new value (and the new value's type). 528 * 529 * NB: This form of the constructor allows the caller to specify 530 * user-defined functions / functors for setting and getting the 531 * value. We do not rely on implicit values for the set/get 532 * functors becausesome compilers (notably, ICC 10.1) interpret 533 * "Property(set, get)" as a bound property 534 * "Property<set_fcn_t>(bound_value=set, set=get, get=get_fcn_t())" 535 */ Property(set_fcn_t set,get_fcn_t get)536 explicit Property( set_fcn_t set, get_fcn_t get ) 537 : ReadOnly_Property(UntypedAnyContainer(), set, get) 538 {} 539 540 /// Create a property that is bound to the provided external data source 541 /** This creates an typed property that is bound to a specific 542 * external data source. Any assignment to this property will 543 * convert the new value into the bound type and store it in the 544 * bound external data source. 545 * 546 * NB: using externally-bound data sources opens you to the 547 * possibility that the external source may be changed directly 548 * without calling the validate() or onChange() events. 549 * 550 * NB: This relies on the default constructor for the set_fcn_t and 551 * get_fcn_t(). If we are compiling with Boost, this will create an 552 * empty() function object. If we are compiling without Boost, per 553 * the C++ standard, the default constructor for pointer types is a 554 * NULL pointer (get() / set() relies on this). 555 */ 556 template<typename T> Property(T & bound_value)557 explicit Property( T& bound_value ) 558 : ReadOnly_Property(Any(bound_value,true,true), set_fcn_t(), get_fcn_t()) 559 {} 560 561 /// Create a property that is bound to the provided external data source 562 /** This creates an typed property that is bound to a specific 563 * external data source. Any assignment to this property will 564 * convert the new value into the bound type and store it in the 565 * bound external data source. 566 * 567 * NB: using externally-bound data sources opens you to the 568 * possibility that the external source may be changed directly 569 * without calling the validate() or onChange() events. 570 */ 571 template<typename T> Property(T & bound_value,set_fcn_t set,get_fcn_t get)572 explicit Property( T& bound_value, set_fcn_t set, get_fcn_t get ) 573 : ReadOnly_Property(Any(bound_value, true, true), set, get) 574 {} 575 576 /// Create a property that is bound to a specific data type 577 /** This creates an typed property that is not bound to a specific 578 * external data source. Any assignment to this property will 579 * convert the new value into the bound type and store it within 580 * this property. Note: use the Bind() static method to crete an 581 * unbound typed property; for example, to create an unbound 582 * Property of fixed type double, use: 583 * \code 584 * utilib::Property property(utilib::Property::Bind<double>()); 585 * \endcode 586 * 587 * NB: This relies on the default constructor for the set_fcn_t and 588 * get_fcn_t(). If we are compiling with Boost, this will create an 589 * empty() function object. If we are compiling without Boost, per 590 * the C++ standard, the default constructor for pointer types is a 591 * NULL pointer (get() / set() relies on this). 592 */ 593 explicit Property(Property::BoundType type, 594 set_fcn_t set = set_fcn_t(), get_fcn_t get = get_fcn_t()) 595 : ReadOnly_Property(type.handle, set, get) 596 {} 597 ~Property()598 virtual ~Property() 599 {} 600 601 602 template<typename T> Bind()603 static BoundType Bind() 604 { 605 BoundType tmp; 606 tmp.handle.template set_immutable<T>(); 607 return tmp; 608 } 609 template<typename T> Bind(T value)610 static BoundType Bind(T value) 611 { 612 BoundType tmp; 613 tmp.handle.template set<T>(value, false, true); 614 return tmp; 615 } 616 617 /// Set the property value set(utilib::Any new_value)618 void set(utilib::Any new_value) 619 { set_impl(new_value, data->is_writable); } 620 621 #ifdef UTILIB_HAVE_BOOST 622 /// Typedef for a functor bound to this Property's set() operation 623 typedef onChange_t::slot_function_type bound_set_t; 624 /// Utility to return a bound set() suitable for the onChange callback 625 bound_set_t bind_set(); 626 #endif 627 628 /// Return this Property within an AnyRef asAnyRef()629 virtual utilib::AnyRef asAnyRef() const 630 { return utilib::AnyRef(*this); } 631 632 /// Return this Property within an AnyFixedRef asAnyFixedRef()633 virtual utilib::AnyFixedRef asAnyFixedRef() const 634 { return utilib::AnyFixedRef(*this); } 635 636 /// Convenience method for implicitly setting the value of a property 637 Property& operator=(utilib::Any new_value) 638 { 639 set_impl(new_value, data->is_writable); 640 return *this; 641 } 642 643 Property& operator=(ReadOnly_Property &rhs) 644 { 645 ReadOnly_Property::operator=(rhs); 646 return *this; 647 } 648 649 protected: 650 651 /// Set the property value 652 void set_impl(utilib::Any new_value, bool writable); 653 654 }; 655 656 /// Special case: compilers pick up this template when copy constructing 657 template<> 658 Property:: 659 Property(const Privileged_Property& rhs); 660 661 /// Special case: compilers pick up this template when copy constructing 662 template<> 663 Property:: 664 Property(Property& rhs); 665 666 /// Special case: compilers pick up this template when copy constructing 667 template<> 668 Property:: 669 Property(Privileged_Property& rhs); 670 671 672 /** The utilib::Privileged_Property class is a simple derivative of a 673 * "normal" Property that provides control over the "is_writable" flag. 674 * It introduces methods for setting and clearing the is_writable flag, 675 * as well as a set() method that will always succeed, regardless of 676 * the current is_writable state. 677 * 678 * This class also supports the following use case: suppose I have a 679 * class that wants to have a public ReadOnly property "Foo". Foo is 680 * used by consumers of a class both to check on the state of Foo and 681 * to register onChange (or even validate) callbacks. To make sure 682 * that the onChange callbacks are always called, the data is stored 683 * within an unbound typed property. In addition, the property is 684 * added to the class's base class's PropertyDict. The "catch" is that 685 * the owning class still needs a handle on the Property though which 686 * it can set the property's value. Further, derived classes may want 687 * to relax the read-only restriction for Foo. To accomplish this, the 688 * class will declare a public ReadOnly Foo and a protected Privileged 689 * _Foo, both bound to the same data: 690 * 691 * \code 692 * class MyClass : public MyBase 693 * { 694 * protected: 695 * Privileged_Property _Foo; 696 * 697 * public: 698 * ReadOnly_Property Foo; 699 * 700 * MyClass() 701 * : _Foo(Property::Bind<double>()), 702 * Foo(_Foo.set_readonly()) 703 * { 704 * MyBase::properties.declare("Foo", _Foo); 705 * } 706 * }; 707 * \endcode 708 */ 709 class Privileged_Property : public Property 710 { 711 public: 712 class onChangeLoopData 713 { 714 private: 715 friend class Privileged_Property; configure(Privileged_Property * _owner,size_t _id)716 void configure(Privileged_Property* _owner, size_t _id) 717 { 718 owner = _owner; 719 id = _id; 720 } 721 722 Privileged_Property* owner; 723 size_t id; 724 725 public: onChangeLoopData()726 onChangeLoopData() 727 : owner(NULL), id(0) 728 {} 729 730 ~onChangeLoopData(); 731 property()732 Privileged_Property* property() 733 { return owner; } 734 }; 735 typedef ReferenceCounted<onChangeLoopData> onChangeLoop_t; 736 737 public: 738 /// Create an unbound untyped property Privileged_Property()739 Privileged_Property() 740 : Property() 741 {} 742 743 /// Explicit copy constructor (to prevent accidental wrapping) Privileged_Property(const Privileged_Property & rhs)744 Privileged_Property(const Privileged_Property& rhs) 745 : Property(rhs) 746 {} 747 748 /// Create an unbound untyped property Privileged_Property(set_fcn_t set,get_fcn_t get)749 explicit Privileged_Property( set_fcn_t set, get_fcn_t get ) 750 : Property(set, get) 751 {} 752 753 /// Create a property that is bound to the provided value 754 template<typename T> Privileged_Property(T & bound_value)755 explicit Privileged_Property( T& bound_value ) 756 : Property(bound_value) 757 {} 758 759 /// Create a property that is bound to the provided value 760 template<typename T> Privileged_Property(T & bound_value,set_fcn_t set,get_fcn_t get)761 explicit Privileged_Property( T& bound_value, set_fcn_t set, get_fcn_t get ) 762 : Property(bound_value, set, get) 763 {} 764 765 /// Create an unbound typed property 766 explicit Privileged_Property( Property::BoundType type, 767 set_fcn_t set = set_fcn_t(), 768 get_fcn_t get = get_fcn_t() ) Property(type,set,get)769 : Property(type, set, get) 770 {} 771 ~Privileged_Property()772 virtual ~Privileged_Property() 773 {} 774 775 776 /// Allow setting the property value regardless of the is_writable flag set(utilib::Any new_value)777 void set(utilib::Any new_value) 778 { Property::set_impl(new_value, true); } 779 780 #ifdef UTILIB_HAVE_BOOST 781 /// Utility to return a bound set() suitable for the onChange callback 782 bound_set_t bind_set(); 783 #endif 784 785 /// Explicit conversion; see ReadOnly_Property::operator Any() const Any()786 virtual operator utilib::Any() const 787 { return get(); } 788 789 /// Return this Privileged_Property within an AnyRef asAnyRef()790 virtual utilib::AnyRef asAnyRef() const 791 { return utilib::AnyRef(*this); } 792 793 /// Return this Privileged_Property within an AnyFixedRef asAnyFixedRef()794 virtual utilib::AnyFixedRef asAnyFixedRef() const 795 { return utilib::AnyFixedRef(*this); } 796 797 /// Convenience method for implicitly setting the value of a property 798 Privileged_Property& operator=(utilib::Any new_value) 799 { 800 Property::set_impl(new_value, true); 801 return *this; 802 } 803 804 /// Calling this method allows setting the value through Property::set() set_readonly()805 ReadOnly_Property& set_readonly() 806 { 807 data->is_writable = false; 808 return *this; 809 } 810 811 /// Calling this method disallows setting the value through Property::set() unset_readonly()812 Property& unset_readonly() 813 { 814 data->is_writable = true; 815 return *this; 816 } 817 818 /// Allow the onChange callback to recurse (up to max times) 819 onChangeLoop_t allowOnChangeRecursion(size_t max = 1); 820 }; 821 822 /// Special case: casting a Property to "void" should do nothing. 823 template<> 824 void ReadOnly_Property::as<void>() const; 825 826 /// Special case: casting a Property to "Any" should return the contained value 827 template<> 828 utilib::Any ReadOnly_Property::as<utilib::Any>() const; 829 830 /// Special case: casting a Property to "Any" should do nothing. 831 template<> 832 utilib::AnyRef ReadOnly_Property::as<utilib::AnyRef>() const; 833 834 /// Special case: casting a Property to "Any" should do nothing. 835 template<> 836 utilib::AnyFixedRef ReadOnly_Property::as<utilib::AnyFixedRef>() const; 837 838 839 /// Special case: compilers pick up this template when copy constructing 840 template<> 841 Privileged_Property:: 842 Privileged_Property(Privileged_Property& rhs); 843 844 845 /// Special case: compilers pick up this template when comparing properties 846 template<> 847 bool ReadOnly_Property::operator==(const ReadOnly_Property& rhs) const; 848 849 /// Special case: compilers pick up this template when comparing properties 850 template<> 851 bool ReadOnly_Property::operator==(const Property& rhs) const; 852 853 /// Special case: compilers pick up this template when comparing properties 854 template<> 855 bool ReadOnly_Property::operator==(const Privileged_Property& rhs) const; 856 857 /// Special case: compilers pick up this template when comparing anys 858 template<> 859 bool ReadOnly_Property::operator==(const Any& rhs) const; 860 861 /// Special case: compilers pick up this template when comparing anys 862 template<> 863 bool ReadOnly_Property::operator==(const AnyRef& rhs) const; 864 865 /// Special case: compilers pick up this template when comparing anys 866 template<> 867 bool ReadOnly_Property::operator==(const AnyFixedRef& rhs) const; 868 869 /// Special case: compilers pick up this template when comparing properties 870 template<> 871 bool ReadOnly_Property::operator<(const ReadOnly_Property& rhs) const; 872 873 /// Special case: compilers pick up this template when comparing properties 874 template<> 875 bool ReadOnly_Property::operator<(const Property& rhs) const; 876 877 /// Special case: compilers pick up this template when comparing properties 878 template<> 879 bool ReadOnly_Property::operator<(const Privileged_Property& rhs) const; 880 881 /// Special case: compilers pick up this template when comparing anys 882 template<> 883 bool ReadOnly_Property::operator<(const Any& rhs) const; 884 885 /// Special case: compilers pick up this template when comparing anys 886 template<> 887 bool ReadOnly_Property::operator<(const AnyRef& rhs) const; 888 889 /// Special case: compilers pick up this template when comparing anys 890 template<> 891 bool ReadOnly_Property::operator<(const AnyFixedRef& rhs) const; 892 893 894 // Properties are printable and comparable within Anys... 895 DEFINE_DEFAULT_ANY_PRINTER(ReadOnly_Property); 896 DEFINE_DEFAULT_ANY_PRINTER(Property); 897 DEFINE_DEFAULT_ANY_PRINTER(Privileged_Property); 898 DEFINE_DEFAULT_ANY_COMPARATOR(ReadOnly_Property); 899 DEFINE_DEFAULT_ANY_COMPARATOR(Property); 900 DEFINE_DEFAULT_ANY_COMPARATOR(Privileged_Property); 901 902 // NB: of course, ReadOnly_Property cannot read 903 DEFINE_DEFAULT_ANY_READER(Property); 904 DEFINE_DEFAULT_ANY_READER(Privileged_Property); 905 906 907 /// Method for printing out the value of properties 908 std::ostream& operator<<(std::ostream& os, 909 const utilib::ReadOnly_Property& property); 910 911 /// Method for reading into properties 912 std::istream& operator>>( std::istream& is, utilib::Property& property ); 913 914 } // namespace utilib 915 916 917 918 #ifdef CXXTEST_RUNNING 919 #include <cxxtest/ValueTraits.h> 920 namespace CxxTest { 921 922 /// Printing utility for use in CxxTest unit tests 923 CXXTEST_TEMPLATE_INSTANTIATION 924 class ValueTraits<utilib::ReadOnly_Property> 925 { 926 public: ValueTraits(const utilib::ReadOnly_Property & p)927 ValueTraits( const utilib::ReadOnly_Property &p ) 928 { 929 std::ostringstream ss; 930 ss << p; 931 str = ss.str(); 932 } 933 asString()934 const char* asString() const 935 { return str.c_str(); } 936 937 private: 938 std::string str; 939 }; 940 941 CXXTEST_TEMPLATE_INSTANTIATION 942 class ValueTraits<utilib::Property> 943 : public ValueTraits<utilib::ReadOnly_Property> 944 { 945 public: ValueTraits(const utilib::Property & p)946 ValueTraits(const utilib::Property& p) 947 : ValueTraits<utilib::ReadOnly_Property>(p) 948 {} 949 }; 950 951 CXXTEST_TEMPLATE_INSTANTIATION 952 class ValueTraits<utilib::Privileged_Property> 953 : public ValueTraits<utilib::Property> 954 { 955 public: ValueTraits(const utilib::Privileged_Property & p)956 ValueTraits(const utilib::Privileged_Property& p) 957 : ValueTraits<utilib::Property>(p) 958 {} 959 }; 960 961 } // namespace CxxTest 962 #endif // CXXTEST_RUNNING 963 964 #endif // defined utilib_Property_h 965