Click here to Skip to main content
15,861,125 members
Articles / Desktop Programming / MFC
Article

Generic C++ Properties

Rate me:
Please Sign up or sign in to vote.
4.53/5 (9 votes)
9 Jun 2002CPOL2 min read 140.9K   34   32
A system for defining properties that is not compiler specific

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:

C++
#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:

C++
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

C++
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

C++
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:

C++
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)


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralYou can eliminate the 1-byte problem with another trick (templates make things a bit cleaner for the optimizers too) Pin
Member 43482046-Dec-08 1:31
Member 43482046-Dec-08 1:31 
QuestionWhat is offset? I implemented all like this, please comment Pin
Axiom66622-May-08 5:54
Axiom66622-May-08 5:54 
QuestionPut functions for assignment not correct ? Pin
davefiete11-Jan-07 5:33
davefiete11-Jan-07 5:33 
AnswerRe: Put functions for assignment not correct ? Pin
Jason.King.Work@gmail.com11-Jan-07 7:27
Jason.King.Work@gmail.com11-Jan-07 7:27 
GeneralRe: Put functions for assignment not correct ? Pin
davefiete11-Jan-07 8:41
davefiete11-Jan-07 8:41 
GeneralExcellent article...YET A simple way to implement properties.... Pin
AnandChavali21-Dec-06 0:15
AnandChavali21-Dec-06 0:15 
GeneralRe: Excellent article...YET A simple way to implement properties.... Pin
AnandChavali21-Dec-06 0:17
AnandChavali21-Dec-06 0:17 
GeneralRe: Excellent article...YET A simple way to implement properties.... Pin
AnandChavali21-Dec-06 0:18
AnandChavali21-Dec-06 0:18 
GeneralRe: Excellent article...YET A simple way to implement properties.... Pin
Jason.King.Work@gmail.com11-Jan-07 7:36
Jason.King.Work@gmail.com11-Jan-07 7:36 
GeneralProperties by preprocessing Pin
Boraski14-Jul-03 22:42
Boraski14-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 | :(

GeneralRe: Properties by preprocessing Pin
Dragon Lord24-Jul-03 16:33
Dragon Lord24-Jul-03 16:33 
GeneralRe: Properties by preprocessing Pin
Boraski27-Jul-03 20:35
Boraski27-Jul-03 20:35 
GeneralA small bug in DECLARE_PROPERTY() Pin
19-Jun-02 2:59
suss19-Jun-02 2:59 
Questionand macros? Pin
10-Jun-02 21:13
suss10-Jun-02 21:13 
AnswerRe: and macros? Pin
Jason.King.Work@gmail.com8-Jul-02 12:41
Jason.King.Work@gmail.com8-Jul-02 12:41 
GeneralGet rid of the pThis pointer Pin
Matthias Mann2-Jun-02 6:53
Matthias Mann2-Jun-02 6:53 
GeneralRe: Get rid of the pThis pointer Pin
Jim Crafton2-Jun-02 7:17
Jim Crafton2-Jun-02 7:17 
GeneralRe: Get rid of the pThis pointer Pin
Matthias Mann2-Jun-02 8:23
Matthias Mann2-Jun-02 8:23 
GeneralRe: Get rid of the pThis pointer Pin
Jim Crafton3-Jun-02 2:50
Jim Crafton3-Jun-02 2:50 
GeneralRe: Get rid of the pThis pointer Pin
Jason.King.Work@gmail.com2-Jun-02 10:20
Jason.King.Work@gmail.com2-Jun-02 10:20 
GeneralExcellent! Pin
Jörgen Sigvardsson2-Jun-02 5:37
Jörgen Sigvardsson2-Jun-02 5:37 
GeneralWrong rating!!! <--- the mathematics I mean Pin
Nish Nishant2-Jun-02 0:15
sitebuilderNish Nishant2-Jun-02 0:15 
GeneralRe: Wrong rating!!! <--- the mathematics I mean Pin
Jörgen Sigvardsson2-Jun-02 5:36
Jörgen Sigvardsson2-Jun-02 5:36 
GeneralRe: Wrong rating!!! <--- the mathematics I mean Pin
Rama Krishna Vavilala2-Jun-02 5:44
Rama Krishna Vavilala2-Jun-02 5:44 
GeneralRe: Wrong rating!!! <--- the mathematics I mean Pin
Jörgen Sigvardsson2-Jun-02 6:06
Jörgen Sigvardsson2-Jun-02 6:06 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.