Just a thought that woke me up this morning. A means of providing 'property' type constructs in C++ classes similar to python properties.

Header

#ifndef _HXX_CPROP
#define _HXX_CPROP

/**
 * Helper class to connect a 'property' in a 
 * c++ class to getter/setter methods
 *
 */
template<class T, typename V>
class property {
public:
  /** 
   * \brief ctor
   */
  property(T* _target, 
       V (T::*_getter)(void) const = (V (T::*)(void))0L,
       V (T::*_setter)(V value) = (V (T::*)(V value))0L) ;
  /**
   * assignment operator.  Provides setter
   *  parent.field = value
   * functionality
   */
  V operator=(V value) ;

  /**
   * coveration operator, provides 'getter' 
   */
  operator V() const ;
private:
  T *target ;
  V (T::*getter)(void) const ;
  V (T::*setter)(V value) ;
} ;

/*
 *
 */
template<class T, typename V>
property<T,V>::property(T* _target, 
            V (T::*_getter)(void) const /* = (void *)0L */,
            V (T::*_setter)(V value)    /* = (void *)0L */)
  : target(_target),
    getter(_getter),
    setter(_setter)
{
}

/*
 *
 */
template<class T, typename V>
inline V property<T,V>::operator=(V value)
{
  return (target->*setter)(value) ;
}

/*
 *
 */
template<class T, typename V>
inline property<T,V>::operator V() const
{
  return (target->*getter)() ;
}

#endif /* _HXX_CPROP */

Test Case

#include <iostream>
#include <string>
#include "cprop.hxx"

using namespace std ;

class testClass {
public:
  testClass() ;
  property<testClass, bool> flag ;

private:
  bool _flag ;
  bool setFlag(bool f) ;
  bool getFlag() const ;
} ;

testClass::testClass()
  : flag(this, &testClass::getFlag, &testClass::setFlag),
    _flag(false)

{
}

bool testClass::setFlag(bool f)
{
  _flag = f ;
  if( _flag )
    cout << "flag on" << endl ;
  else
    cout << "flag off" << endl ;
  return f ;
}

bool testClass::getFlag() const
{
  return _flag ;
}


int main(int argc, char **argv)
{

  testClass c ;

  c.flag = true ;
  c.flag = false ;
  c.flag = true ;

  cout << "flag state = " << c.flag << endl ;

}

I don't mean to burst your bubble, but this kind of thing has been around for a long while, and are used quite a bit.

Take, for example, the Boost.Property-Map library which has generic concepts for mapping keys to properties, either in the same way as you use std::map, or more general things. For example, I have in my library some data-member properties, similar to what you presented here.

Then, there are things more along the flavor of Boost.Parameter where you can give names to parameters and set them by name as you feed them to a constructor / function / setter-getter.

Funny you mention Python, because the Boost.Python library also allows you to declare a set of properties for the classes you export for a Python library.

Most plugin systems have some mechanism to created "published" properties. For example, Qt has a pretty complex property system in order to publish properties of custom widgets. COM objects have similar accomodations in their interfaces.

Then, an alternate but interesting approach that I have been exploring lately is the use of a serialization library to automatically create "schemas" for classes (e.g., like XML Schemas). I already have a protobuf schema generator (takes all the classes that exist in your program and generates a schema for them all), and one for constructing an object property tree (which I plan to use for some kind of general object-inspector + property-editor application). Since most decent libraries use a serialization method already, it is a very non-intrusive way of getting a basic list of properties that can be edited.

Finally, some compilers have extensions for declaring properties, which kind of mimic the way properties exist in Delphi.

As per your actual code, here are a few improvements I would recommend:

#ifndef _HXX_CPROP
#define _HXX_CPROP

/**
 * Helper class to connect a 'property' in a 
 * c++ class to getter/setter methods
 * NOTE: Be more explicit about what the template arguments are:
 * \tparam ClassType   The type of the class in which the property is found.
 * \tparam MemberType  The type of the property value.
 */
template <typename ClassType, typename MemberType> 
class property {
public:
  typedef property<ClassType, MemberType> self; // good to have a "self" type.
  typedef MemberType (ClassType::*getter_type)() const;
  typedef void (ClassType::*setter_type)(const MemberType&);

  /** 
   * \brief ctor
   * NOTE: Avoid using leading underscores, technically, the C++ standard forbids them.
   */
  property(ClassType* aTarget, 
           getter_type aGetter = NULL,
           setter_type aSetter = NULL) :
           target(aTarget),
           getter(aGetter),
           setter(aSetter) { 
    if(!getter)
      throw std::logic_error("Cannot have a NULL getter function for a property!");
  };

  /**
   * assignment operator.  Provides setter
   *  parent.field = value
   * functionality
   */
  self& operator=(const MemberType& value) {
    if(setter)  // for non-nullity of setter pointer.
      (target->*setter)(value);
    return *this;
  };

  /**
   * conversion operator, provides 'getter' 
   */
  operator MemberType() const {
    return (target->*getter)();
  };
private:
  ClassType* target;
  getter_type getter;
  setter_type setter;
};

#endif /* _HXX_CPROP */

And you would probably want to provide functions to check if the property is read-only or not, and similar things. Moreover, you should probably create some abstractions for the properties (and possibly, type erasure in this case), in case people want to define a property that isn't necessarily accessible through a simple setter-getter on a class. Something like this perhaps:

template <typename ValueType>
class property {
  // ... some interface:
  public:
    virtual ValueType getValue() const = 0;
    virtual bool isReadOnly() const { return true; };
    virtual void setValue(const ValueType&) { };
    // .. maybe some more things like possibly 'toString()' and 'fromString()'.
};

template <typename ClassType, typename MemberType>
class setget_class_property : public property<MemberType> {
  public:
    virtual ValueType getValue() const {
      return (target->*getter)();
    };
    virtual bool isReadOnly() const { 
      return (!setter); 
    };
    virtual void setValue(const ValueType&) {
      if(setter)  // for non-nullity of setter pointer.
      (target->*setter)(value);
    };

    // same as before for the rest..
};

This is going to be more convenient to hide away the actual detail of how the property is set and get. And, it also allows people to define their own methods for setting and getting the properties.

Yeah, that'll teach me to post off the top of my head. Consider me chassened, disciplined, served, schooled and broken.

Yeah, that'll teach me to post off the top of my head. Consider me chassened, disciplined, served, schooled and broken.

Please don't feel that way. It was an interesting post and a good piece of code that you showed. If you want to discuss it more or have other ideas, please do so.

My post was just intended to suggest things that you might want to look into to get inspiration on improvements or applications for your idea. I didn't mean to put you off or anything, just trying to feed the discussion about an interesting topic.

 testClass c ;
 c.flag = true ;
 c.flag = false ;
 c.flag = true ;

Can someone explain why this would be a good idea? It seems like a lot of work for little benifit.

Can someone explain why this would be a good idea?

It's a little more natural syntax for setters and getters.

It seems like a lot of work for little benifit.

Indeed. I personally wouldn't use it unless the problem domain suggests a distinct benefit, but I also have a tendency to oversimplify. ;) Properties are nice to have, but generally only if the language supports them natively.

It's a little more natural syntax for setters and getters.

I guess it could be considered more natural by some. But in my opinion for stronlgy typed language such as C++ which has concepts of public/private/protected it would be more natural for me to call a member function instead of just using the wrapped variable. But that's just my opinion. Maybe I say that because that is how I was taught and started learning.

Edited 3 Years Ago by firstPerson

This question has already been answered. Start a new discussion instead.