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