Implementing a Property in C++

Environment: Visual C++ 7.0, Windows XP sp1, Windows 2000 sp3

Abstract

This project tries to simulate the property behavior that exists in C# (and other languages...) in C++, without using any extensions. Most libraries or compilers that implement properties in C++ use extensions, as in Managed C++ or C++ Builder, or they use the set and get methods that look like normal methods, not properties.

Details

Let's first see what a property is. A property acts like a field or member variable, in that is uses the library, but it accesses the underlying variable through the read or write methods or the set or get methods.

For example, if I have class A and property Count, I can write the following code:

  A foo;

  cout  << foo.Count;

The Count actualy calls the get function that returns the value of the required variable. One of the main advantages of using a property instead of directly using the variable is that you can set the property as read-only (you are allowed only to read the variable without changing it), write-only, or both read and write. So, let's begin implementing it:

We need to be able to do the following:

    int i = foo.Count;    //-- Will call the get function to
                          //   get the underlying value --

    foo.Count = i;        //-- Will call the set function to set
                          //   the value --

So, it obvious that we need to overload the '=' sign to set the variable, and also the return type (which we will now have later).

We will implement a class called property, which will act exactly like a property, as in the following:

template<typename Container, typename ValueType, int nPropType>

class property {}

This Template class will represent our property. The Container is the type of class that will contain the variable, the set & get methods, and the property. ValueType is the type of the internal variable itself, and nPropType is the access type of the property read-only, write-only, or read/write.

Now we need to set a pointer to the set and get methods from the container class to the property and also override the '=' operator so that the property will act like the variable. So, let's see the full listing of the property class:

#define READ_ONLY 1
#define WRITE_ONLY 2
#define READ_WRITE 3

template <typename Container, typename ValueType, int nPropType>
class property
{
public:
property()
{
  m_cObject = NULL;
  Set = NULL;
  Get = NULL;
}
//-- This to set a pointer to the class that contain the
//   property --
void setContainer(Container* cObject)
{
  m_cObject = cObject;
}
//-- Set the set member function that will change the value --
void setter(void (Container::*pSet)(ValueType value))
{
  if((nPropType == WRITE_ONLY) || (nPropType == READ_WRITE))
    Set = pSet;
  else
    Set = NULL;
}
//-- Set the get member function that will retrieve the value --
void getter(ValueType (Container::*pGet)())
{
  if((nPropType == READ_ONLY) || (nPropType == READ_WRITE))
    Get = pGet;
  else
    Get = NULL;
}
//-- Overload the '=' sign to set the value using the set
//   member --
ValueType operator =(const ValueType& value)
{
  assert(m_cObject != NULL);
  assert(Set != NULL);
  (m_cObject->*Set)(value);
  return value;
}
//-- To make possible to cast the property class to the
//   internal type --
operator ValueType()
{
  assert(m_cObject != NULL);
  assert(Get != NULL);
  return (m_cObject->*Get)();
}
private:
  Container* m_cObject;  //-- Pointer to the module that
                         //   contains the property --
  void (Container::*Set)(ValueType value);
                         //-- Pointer to set member function --
  ValueType (Container::*Get)();
                         //-- Pointer to get member function --
};

So, now let's see it piece by piece:

In the following code, just set the Container pointer to a valid instance that will have the property:

void setContainer(Container* cObject)
{
  m_cObject = cObject;
}

In the following code, set a pointer to the get and set member functions of the container class. The only restriction is that the set must have a single parameter and return void and the get no paramter, but return the value type:

//-- Set the set member function that will change the value --
void setter(void (Container::*pSet)(ValueType value))
{
  if((nPropType == WRITE_ONLY) || (nPropType == READ_WRITE))
    Set = pSet;
  else
    Set = NULL;
}
//-- Set the get member function that will retrieve the value --
void getter(ValueType (Container::*pGet)())
{
  if((nPropType == READ_ONLY) || (nPropType == READ_WRITE))
    Get = pGet;
  else
    Get = NULL;
}

In the following code, the first operator is the '='; it calls the set member of the container and gives it the value. The second part is to make the entire property class act as the ValueType so it returns the return of the get function:

//-- Overload the '=' sign to set the value using the set member --
ValueType operator =(const ValueType& value)
{
  assert(m_cObject != NULL);
  assert(Set != NULL);
  (m_cObject->*Set)(value);
  return value;
}
//-- To make possible to cast the property class to the
//   internal type --
operator ValueType()
{
  assert(m_cObject != NULL);
  assert(Get != NULL);
  return (m_cObject->*Get)();
}

So, let's see how to use it now:

As shown below, the PropTest class implements a simple property called Count. The actual value will be stored and retrieved from the private member variable "m_nCount", through the get and set methods. The get and set methods can have any name as long as their address is passed to the property class as shown in the constructor of the PropTest, in the line "property<PropTest,int,READ_WRITE> Count; " that we have a read & write property of type integer in class PropTest called Count. Now you can call Count as a normal member variable, but in fact you call the set and get methods indirectly.

The initialization shown in the constructor of the PropTest class is necessary for the property class to work.

class PropTest
{
public:
  PropTest()
  {
    Count.setContainer(this);
    Count.setter(&PropTest::setCount);
    Count.getter(&PropTest::getCount);
  }
  int getCount()
  {
    return m_nCount;
  }
  void setCount(int nCount)
  {
    m_nCount = nCount;
  }
  property<PropTest,int,READ_WRITE> Count;


private:
  int m_nCount;
};

As shown below, you use the Count property as if it were a normal variable.

int i = 5,j;
PropTest test;
test.Count = i;    //-- call the set method --
j= test.Count;     //-- call the get method --

For read-only, you create an instance for the property as follows:

property<PropTest,int,READ_ONLY > Count;

For write-only, you create an instance for the property as follows:

property<PropTest,int,WRITE_ONLY > Count;
Note: If you set the property to read-only and you try to write to it, it will cause an assertion. The same will occur if you set the property to write-only and you try to read it.

Conclusion

This article showed you how to implement a property in C++, using standard C++ without any extension. Of course, directly using the set and get methods is more efficient because with this method you have a new instance of the property class per each property.

Downloads

Download demo project - 100 Kb


Comments

  • Another solution ...

    Posted by Loic on 05/14/2012 08:11am

    A simpler way is to defines both get/set functions as a Property function like : Class Y{ inline const std::string & Prop(){return m_value;}; inline void Prop(const std::string value){m_prop = value;}; }; Using one or the other provide the same functionality, except it's a function call i.e. : int main(int argc, char ** argv) { Y y; y.Prop() = value; // Setter cout

    Reply
  • Yet a simple way to implement properties in C++

    Posted by anand_chavali on 12/21/2006 11:58pm

    /* Please read "less than or greater than" symbols for "( and )" where ever appropriate */
    #include (tchar.h)
    #include (conio.h)
    #include (iostream)
    #include (string)
    #include (vector)
    using namespace std;
    
    template(typename T, char PropertyType) class Property {
    private:
        T myVar;    
    public:    
        Property() {
            if ( PropertyType != 'r' && PropertyType != 'w' && PropertyType != 'b' ) {            
                throw "Invalid PropertyType. PropertyTypes allowed are :: \n\tr ->Read , w -> Write , b -> Both Read and Write.";
            } 
        }
          operator T() {
              return GetValue();
        }
        void operator = ( T theVar ) {
            SetValue(theVar);
        }
        virtual T GetValue() {
            if ( PropertyType == 'w' ) {
                 throw "Cannot read from a write-only property";
             }
            return myVar;
        }
        virtual void SetValue(T theVar) {
            if ( PropertyType == 'r' ) {
                 throw "Cannot write to a read-only property";        
            }
            myVar = theVar;
        }
    };
    
    class PropertyUser {
    public:
        Property(int, 'b') IntProperty;
        void Display() {
            cout << "\nPropertyUser::Display() --> Displaying IntProperty :: " << IntProperty;
        }
    };
    int _tmain(int argc, _TCHAR* argv[]) {
        try
        {
            Property(string ,'b') aStringProperty;
            aStringProperty = "Anand";
            string aStrProp = aStringProperty;      
            cout << "Previosly assigned :: " << "Anand  " << "Retrived from property :: " << aStrProp;
    
            Property , 'b'> aVectorProperty;
            vector aVector;
            aVector.push_back(1);aVector.push_back(2);aVector.push_back(3);aVector.push_back(4);aVector.push_back(5);
            aVectorProperty = aVector;
            cout << "\n\nInitialised vector with values 1,2,3,4,5.  Retrieving from vector property ::\n";
            for ( int i = 0 ; i < 5 ; i++ ) {
                cout << aVectorProperty.GetValue() [ i ] << endl;
            }   
    
            cout << "\n\nNow Testing with class : \n";
            PropertyUser aUser;
            aUser.IntProperty = 10;
            aUser.Display();
    
            cout << "\nDisplaying PropertyUser's IntProperty in main :: " << aUser.IntProperty;
        } catch( char* theException ) {
            cout <<  "\n\nException Occured : \n--------------------\n        Message : " << theException ;
        }
        _getch();
    	return 0;
    }

    • properties in another simple way

      Posted by guenter68 on 09/21/2009 10:32am

      #include 
      
      using namespace std;
      
      template  class Property
      {
      public: 
      	
      	Property() : Owner(NULL), Get(NULL), Set(NULL), Value(0){}
      
      	// Read-Only, Value can be initialized / otherwise it's zero
      	void Init(OwnerType *pOwner, ValueType (OwnerType::*pGet)(void), ValueType SetVal = 0) 
      	{
      		Owner	= pOwner;
      		Get		= pGet;
      		Value	= SetVal;
      	}
      
      	// Write-Only
      	void Init(OwnerType *pOwner, void (OwnerType::*pSet)(ValueType setVal)) 
      	{
      		Owner	= pOwner;
      		Set		= pSet;
      	}
      
      	// Random-Access
      	void Init(OwnerType *pOwner, ValueType (OwnerType::*pGet)(void), void (OwnerType::*pSet)(ValueType setVal)) 
      	{
      		Owner	= pOwner;
      		Get		= pGet;
      		Set		= pSet;
      	}
      
      	// Calling 'Set'
      	void operator = (const ValueType &SetVal)
      	{
      		if(Set != NULL) (Owner->*Set)(SetVal);
      		else /* exception */;
      	}
      	
      	// Calling 'Get'
      	operator ValueType()
      	{
      		if(Get != NULL) return (Owner->*Get)();
      		else /* exception */;
      		return 0;
      	}
      	
      	ValueType	Value;
      	OwnerType	*Owner;
      	ValueType	(OwnerType::*Get)(void);
      	void		(OwnerType::*Set)(ValueType);
      };
      
      class Tester
      {
      	typedef Property	PropertyStr;
      	typedef Property		PropertyInt;
      	typedef Property		PropertyFlt;
      		
      public:
      	Tester()
      	{
      		Name.Init		(this, &Tester::GetName, &Tester::SetName);
      		Address.Init	(this, &Tester::GetAddress, reinterpret_cast(this));
      	}
      	
      	PropertyStr Name;
      	char *	GetName()				{return Name.Value;}
      	void	SetName(char *name)		{Name.Value = name;}
      	
      	PropertyInt Address;
      	int 	GetAddress()			{return Address.Value;}
      	
      };
      
      int main()
      {	
      	Tester test;
      	
      	test.Name = "Tester";
      	char * str = test.Name;
      
      	// this won't work, because of Read-Only state (function pointer 'Set' is NULL)
      	test.Address = 234;
      
      	cout << 
      		"str            = " << str << endl << 
      		"test.Name      = " << test.Name << endl << 
      		"test.Address   = " << test.Address << endl << endl;
      	
      	return 0;
      }

      Reply
    Reply
  • Important question

    Posted by Ali Imran on 06/20/2005 04:22pm

    The property is not returning right values when it is used directly within a statement (if it is not typecasted). printf ("%d",test.Count); //returns invalid value (some garbage) printf ("%d",(int)test.Count); //now is fine after typecast I want printf ("%d",test.Count); to work fine so that I don't have to typecast each and every kind of property (especially without assigning it to some other variable). can you please fix it? regards

    Reply
  • Indeed outstanding - really helpful

    Posted by Ali Imran on 06/20/2005 01:41am

    This is the most valuable aticle atleast for me. For me, it is much more better than http://www.codeproject.com/cpp/genericproperty.asp I do not say that the other article is not good, but it is perfect for newbies. regards

    Reply
  • why?

    Posted by Mr.Prakash on 03/03/2004 08:52am

    if you make a member variable of a class public you get the same result. simulating the same effect differently is not what any one expects in their project. obviously you have not read about property keyword in vc++

    • Exactly

      Posted by Ali Imran on 06/20/2005 01:47am

      This is the major purpose of implementation of this class I think
      
      
      [quote]
      I didn't use the one that come with VC because it is not portable across C++ compiler. I want to make a C++ property implementation that work in all standard compiler. Regards, Emad Barsoum
      [/quote]

      Reply
    • Re: Why?

      Posted by ebarsoum on 03/03/2004 12:29pm

      I read about property keyword in VC++, C++Builder and in other languages like C#, VB and Delphi,and it is not the same effect because in my implementation you can write only get (to be only read access) or only set, but for public variables it will always have full access (read and write). also you can add any code validation or conversion or trigger any method, which you can't do in normal public variable. and I didn't use the one that come with VC because it is not portable across C++ compiler. I want to make a C++ property implementation that work in all standard compiler. Regards, Emad Barsoum

      Reply
    Reply
  • That's really nice.

    Posted by Legacy on 11/10/2003 12:00am

    Originally posted by: AM

    With no doubt the best approach I've seen so far. Really nice.

    Reply
  • Assignment of properties

    Posted by Legacy on 07/10/2003 12:00am

    Originally posted by: Roeland Bekker

    Nice code, I like simple stuff that uses std C++. 
    
    When using the property, I came across a omission: the assignment between properties themselves.
    So, in order to write code like this:

    {
    ...
    property<CClass,int,READ_WRITE> aProperty;
    property<CClass,int,READ_WRITE> anotherProperty;
    ...
    anotherProperty = 1;
    aProperty = anotherProperty;
    ...
    }

    you need an assignment operator for properties. Anyone using the class presented in this article is probably familiar with template programming, but for anyone who is less familiar, template code is not trivial. So here is the implementation:

    //-- Overload the '=' sign to enable assignments between properties --
    property operator =(const property& value)
    {
    assert(m_Container != NULL);
    assert(Set != NULL);
    (m_Container->*Set)((value.m_Container->*value.Get)());
    return *this;
    }
    friend property;

    Add this piece of code somewhere in the property class, and you can assign properties.

    • gcc does not seem to compile it

      Posted by Ali Imran on 09/03/2006 04:17am

      gcc / mingw doesnto seem to compile 'friend property;' regards

      Reply
    Reply
  • Another way...

    Posted by Legacy on 04/02/2003 12:00am

    Originally posted by: Jason King

    Another way to do this can be found here:
    http://www.codeproject.com/cpp/genericproperty.asp

    It's an article I wrote to do the same thing. Good work coming up with another approach.

    Jason King
    The Collective

    - If it walks like a duck and talks like a duck and it's hunting season, then it better duck

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Protecting business operations means shifting the priorities around availability from disaster recovery to business continuity. Enterprises are shifting their focus from recovery from a disaster to preventing the disaster in the first place. With this change in mindset, disaster recovery is no longer the first line of defense; the organizations with a smarter business continuity practice are less impacted when disasters strike. This SmartSelect will provide insight to help guide your enterprise toward better …

  • Hybrid cloud platforms need to think in terms of sweet spots when it comes to application platform interface (API) integration. Cloud Velocity has taken a unique approach to tight integration with the API sweet spot; enough to support the agility of physical and virtual apps, including multi-tier environments and databases, while reducing capital and operating costs. Read this case study to learn how a global-level Fortune 1000 company was able to deploy an entire 6+ TB Oracle eCommerce stack in Amazon Web …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds