Click here to Skip to main content
Click here to Skip to main content

Generic C++ Properties

By , 9 Jun 2002
 

Introduction

Recently one of the new programmers (with a Borland C++ Builder background) here at The Collective (where I work) asked if there was any way to add properties to a class. I mentioned that Visual C++ uses __declspec(property) and that it is not cross compiler compatible. But it seemed to me that there should be a way to create generic properties. So I scanned various web pages including codeproject and found some references to using a struct as a replacement. But what I wanted was a way to seamlessly switch between __declspec(property) and a generic solution. I created some structs that I liked and then I converted them to a macro based implementation:

#define USE_DECLSPEC_PROPERTY 0
#if USE_DECLSPEC_PROPERTY

#define DECLARE_PROPERTY_GET( type, getfunc, base, var )      \
        __declspec( property( get=getfunc ) ) type var
#define DECLARE_PROPERTY_PUT( type, putfunc, base, var )      \
        __declspec( property( put=putfunc ) ) type var
#define DECLARE_PROPERTY( type, getfunc, putfunc, base, var ) \
        __declspec( property( get=getfunc, put=putfunc ) ) type var

#else

#define DECLARE_PROPERTY_GET( type, getfunc, base, var )                             \
struct _dp_##getfunc                                                                 \
{                                                                                    \
    inline operator type()                                                           \
    {                                                                                \
        return ((base *) ((char *) this - offsetof( base, var )))->getfunc();        \
    }                                                                                \
};                                                                                   \
friend struct _dp_##getfunc;                                                         \
_dp_##getfunc var

#define DECLARE_PROPERTY_PUT( type, putfunc, base, var )                             \
struct _dp_##putfunc                                                                 \
{                                                                                    \
    inline type operator=(const type & value)                                        \
    {                                                                                \
        return ((base *) ((char *) this - offsetof( base, var )))->putfunc( value ); \
    }                                                                                \
};                                                                                   \
friend struct _dp_##putfunc;                                                         \
_dp_##putfunc var

#define DECLARE_PROPERTY( type, getfunc, putfunc, base, var )                        \
struct _dp_##getfunc_##putfunc                                                       \
{                                                                                    \
    inline operator type()                                                           \
    {                                                                                \
        return ((base *) ((char *) this - offsetof( base, var )))->getfunc();        \
    }                                                                                \
    inline type operator=(const type & value)                                        \
    {                                                                                \
        return ((base *) ((char *) this - offsetof( base, var )))->putfunc( value ); \
    }                                                                                \
};                                                                                   \
friend struct _dp_##getfunc_##putfunc;                                               \
_dp_##getfunc_##putfunc var
#endif

At this point I figured I would create a template that any CWnd derived class could use to add a bunch of properties:

template <class T> class CWndProperties : public T, public CWndPropertiesImpl  {
public:        
    CWndProperties() : CWndPropertiesImpl( this ) 
    {       
    }
          
    DECLARE_PROPERTY( bool, Wnd_GetShow, Wnd_PutShow, 
                               CWndProperties<T>, wShow );
    DECLARE_PROPERTY( CRect, Wnd_GetRect, Wnd_PutRect, 
                               CWndProperties<T>, wRect ); 
    DECLARE_PROPERTY( DWORD, Wnd_GetStyle, Wnd_PutStyle, 
                               CWndProperties<T>, wStyle ); 
    DECLARE_PROPERTY( DWORD, Wnd_GetStyleEx, Wnd_PutStyleEx,
                               CWndProperties<T>, wStyleEx );
};

To reduce compile times and potential code bloat CWndPropertiesImpl was created:
.h file

class CWndPropertiesImpl          
{                                                 
public:                                           
    CWndPropertiesImpl(CWnd * pWnd = NULL);       
    void Wnd_SetWnd(CWnd * pWnd);                 
                                                  
    bool Wnd_GetShow();                           
    bool Wnd_PutShow(const bool & bShow);         
                                                  
    CRect Wnd_GetRect();                          
    CRect Wnd_PutRect(const CRect & rc);          
                                                  
    DWORD Wnd_GetStyle();                         
    DWORD Wnd_PutStyle(const DWORD & dwStyle);    
                                                  
    DWORD Wnd_GetStyleEx();                       
    DWORD Wnd_PutStyleEx(const DWORD & dwStyleEx);
                                                  
private:                                          
    CWnd * m_pWnd;                                
};                                                

.cpp file

CWndPropertiesImpl::CWndPropertiesImpl(CWnd * pWnd)
{
    m_pWnd = pWnd;
}

void CWndPropertiesImpl::Wnd_SetWnd(CWnd * pWnd)
{
    m_pWnd = pWnd;
}

bool CWndPropertiesImpl::Wnd_GetShow()
{
    return m_pWnd->IsWindowVisible() ? true : false;
}

bool CWndPropertiesImpl::Wnd_PutShow(const bool & bShow)
{
    bool bReturn = Wnd_GetShow();
    m_pWnd->ShowWindow( bShow ? SW_SHOW : SW_HIDE );
    return bReturn;
}

CRect CWndPropertiesImpl::Wnd_GetRect()
{
    CRect rcReturn;
    m_pWnd->GetWindowRect( &rcReturn );
    return rcReturn;
}

CRect CWndPropertiesImpl::Wnd_PutRect(const CRect & rc)
{
    CRect rcReturn = Wnd_GetRect();
    m_pWnd->SetWindowPos( NULL, rc.left, rc.top, 
                             rc.Width(), rc.Height(), 
                             SWP_NOZORDER | SWP_NOACTIVATE );
    return rcReturn;
}

DWORD CWndPropertiesImpl::Wnd_GetStyle()
{
    return m_pWnd->GetStyle();
}

DWORD CWndPropertiesImpl::Wnd_PutStyle(const DWORD & dwStyle)
{
    DWORD dwReturn = Wnd_GetStyle();
    m_pWnd->ModifyStyle( 0xFFFFFFFF, dwStyle );
    return dwReturn;
}

DWORD CWndPropertiesImpl::Wnd_GetStyleEx()
{
    return m_pWnd->GetExStyle();
}

DWORD CWndPropertiesImpl::Wnd_PutStyleEx(const DWORD & dwStyleEx)
{
    DWORD dwReturn = Wnd_GetStyleEx();
    m_pWnd->ModifyStyleEx( 0xFFFFFFFF, dwStyleEx );
    return dwReturn;
}

Now all I needed to do was derive my window class from the CWndProperties template - passing in the class that I originally derived my class from and that was it:

class CNewTreeCtrl : public CWndProperties<CTreeCtrl>
...
CNewTreeCtrl wndTree;
...
wndTree.wShow = false; // this hides the window
wndTree.wShow = true;  // this shows the window
wndTree.wShow = !wndTree.wShow // this toggles the windows visibility

Benefits

As a generic solution, this will work on different machines with different compilers. It can also reduce the API/framework that someone new to (let's say) MFC might need to learn. It is backwards compatible with classes already defined. And, in my opinion, once established the code will look cleaner and easier to read.

Problems

OK, so there are a few problems. First, the generic solution still uses at least 1 byte per property; and on Visual C++ it rounds up to the next 4 byte boundry. Second, the generic solution is not able to do property arrays.

Conclusion

The Collective is a company that makes video games that run on multiple platforms. The use of generic properties is appealing, but will likely have limited use within areas that need to be optimized. I know that I'll use them in the Tools department, but it's adoption in other departments will need to be limited to classes where every ounce of speed or memory is not an issue.

Revision history

June 1st 2002 - Initial revision.
June 4th 2002 - Removed dependency on the (4 byte) pThis variable for every property; it now only requires 1 byte per property. Thanks to Matthias Mann who made this suggestion in his comments.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Jason.King.Work@gmail.com
Web Developer
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralYou can eliminate the 1-byte problem with another trick (templates make things a bit cleaner for the optimizers too)memberMember 43482046 Dec '08 - 1:31 
Using #pragma pack(push,...) #pragma pack(pop) eliminates the natural pack size of 4, 8, etc. Leaving you stuck with 1-byte.
 
However, if you recognize that your struct/class on which you want to add properties already has some "real" field, then you can do the following trick to add an unlimited number of properties at a cost of 0-bytes.
 
#pragma pack(push,1)
typedef struct {} Empty;
template */, typename TBase=Empty>
struct TExample : TBase {
union {
int m_realField; // Assuming you need this
PROPERTY(SomeTypeA, SomeNameA); // Cost Zero for empty struct it creates
PROPERTY(SomeTypeB, SomeNameA); // Cost Zero for empty struct it creates
PROPERTY(SomeTypeC, SomeNameA); // Cost Zero for empty struct it creates
};
};
struct Example : TExample {
SomeTypeA pg_SomeNameA() ...
SomeTypeA ps_SomeNameA() ...
...
};
#pragma pack(pop)

QuestionWhat is offset? I implemented all like this, please commentmemberAxiom66622 May '08 - 5:54 
Will this work on all compilers?
 
The only thing is that now, each property is 2 in size.
 
#define Set(type, name) void _set_##name (type	Value)
 
#define Get(type, name)	type _get_##name ()
 
#define __offset(className, propertyName) (((byte*)&((className*)NULL)->propertyName - (byte*)NULL) + 1)
 
#define Property(type, name, className) \	struct {} __dummy_Property_##name;\
	struct \
	{\
		public:\
		inline void operator =(type __val)\
		{	((className*)(this - __offset(className, __dummy_Property_##name)))->_set_##name(__val); }\
	\
		inline operator type()\
		{	return (type)((className*)(this - __offset(className, __dummy_Property_##name)))->_get_##name(); }\
	} name
 
class MyClass
{
   int localVariable;
 
   Get(int, MyProperty)
   {
   }
   Set(int, MyProperty)
   {
        throw new exception();
   }
   Property(int, MyProperty, MyClass);
};
 

 
Axiom
 
One of many...

QuestionPut functions for assignment not correct ?memberDaMagic11 Jan '07 - 5:33 
Explicitly for one put function.
 
The following (as depicted in the article) returns the old (previously) set value and not the new one.
 
bool CWndPropertiesImpl::Wnd_PutShow(const bool & bShow)
{
      bool bReturn = Wnd_GetShow();
      m_pWnd->ShowWindow( bShow ? SW_SHOW : SW_HIDE );
      return bReturn;
}
 
Should be implemented as:
 
bool CWndPropertiesImpl::Wnd_PutShow(const bool & bShow)
{
      m_pWnd->ShowWindow( bShow ? SW_SHOW : SW_HIDE );
      return Wnd_GetShow();
}
 
(No reason to duplicate all other puts in a same manner.)
 
In addition it's better that the callee returns the reference to the property (the nested class) back to the caller and not a copy of field of the wrapper class. (Does not work with types which have no copy-constructor).
 
Additionally the nested class' should be implemented each as a proxy class which always behave like the appropriate type. (See design pattern for proxy class).
AnswerRe: Put functions for assignment not correct ?memberJason King11 Jan '07 - 7:27 
More appropriately:
bool CWndPropertiesImpl::Wnd_PutShow(const bool & bShow)
{
    return m_pWnd->ShowWindow( bShow ? SW_SHOW : SW_HIDE );
}
Which is actually more similar to my original function. The intent was to replicate the functionality of ShowWindow which returns whether the window was previously visible.
GeneralRe: Put functions for assignment not correct ?memberDaMagic11 Jan '07 - 8:41 
Hello again,
 
okay, I only want to note some annotations. Nevertheless very nice article and more elegant than the one I found here:
 
http://www.codeguru.com/cpp/cpp/algorithms/general/article.php/c13039/[^]
 
Bye
 

GeneralExcellent article...YET A simple way to implement properties....memberAnandChavali21 Dec '06 - 0:15 

#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;
      }
};
int _tmain(int argc, _TCHAR* argv[]) {
      try
      {
            Property<string ,'e'> aStringProperty;
            aStringProperty = "Anand";
            string aStrProp = aStringProperty;        
            cout << "Previosly assigned :: " << "Anand   " << "Retrived from property :: " << aStrProp;
 
            Property<vector<int> , 'b'> aVectorProperty;
            vector<int> aVector;
            aVector.push_back(1);aVector.push_back(2);aVector.push_back(3);aVector.push_back(4);aVector.push_back(5);
            aVectorProperty = aVector;
            vector<int> aNewVector = aVectorProperty;
            cout << "\n\nInitialised vector with values 1,2,3,4,5.   Retrieving from vector property ::\n";
            for ( int i = 0 ; i < 5 ; i++ ) {
                  cout << aNewVector [ i ] << endl;
            }           
      } catch( char* theException ) {
            cout <<   "\n\nException Occured : \n--------------------\n            Message : " << theException ;
      }
      _getch();
     return 0;
}
 

 
Thanks and Regards,
Anand.
GeneralRe: Excellent article...YET A simple way to implement properties....memberAnandChavali21 Dec '06 - 0:17 
Actual class declaration is "template class Property "
 
Thanks and Regards,
Anand.

GeneralRe: Excellent article...YET A simple way to implement properties....memberAnandChavali21 Dec '06 - 0:18 
Actual class definition is like template(typename T, char PropertyType) class Property
 
I dont know why the "less than symbol and greator than symbol" are dissappearing here ...so i used "(" for them....
 
Thanks and Regards,
Anand.

GeneralRe: Excellent article...YET A simple way to implement properties....memberJason King11 Jan '07 - 7:36 
Thank you for your encouragement.
 
Regarding your implementation, it is valid if all you want to do is wrap a variable to limit read or write. Your system would require more implementation to resolve wrapping random functions which is what my article dealt with.
GeneralProperties by preprocessingmemberBoraski14 Jul '03 - 22:42 
I suggest a different way to implement properties in C++. Why not develop a simple preprocessor program that parses C++ source and substitutes property read accesses by calls to the get function, and write accesses to the set function? There would be a property declaration syntax that can be Microsoft's, Borland's or a new one.
 
It would produce a new source on the fly that can go to any compiler. And there would be no runtime overhead at all.
 
Anyway I would like that extension to be standard, but standard C++ defenders don't like the idea Frown | :( br />

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 10 Jun 2002
Article Copyright 2002 by Jason.King.Work@gmail.com
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid