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

Generic C++ Properties

By , 9 Jun 2002
Rate this:
Please Sign up or sign in to vote.

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
No Biography provided

Comments and Discussions

 
GeneralYou can eliminate the 1-byte problem with another trick (templates make things a bit cleaner for the optimizers too) PinmemberMember 43482046-Dec-08 1:31 
QuestionWhat is offset? I implemented all like this, please comment PinmemberAxiom66622-May-08 5:54 
QuestionPut functions for assignment not correct ? PinmemberDaMagic11-Jan-07 5:33 
AnswerRe: Put functions for assignment not correct ? PinmemberJason King11-Jan-07 7:27 
GeneralRe: Put functions for assignment not correct ? PinmemberDaMagic11-Jan-07 8:41 
GeneralExcellent article...YET A simple way to implement properties.... PinmemberAnandChavali21-Dec-06 0:15 
GeneralRe: Excellent article...YET A simple way to implement properties.... PinmemberAnandChavali21-Dec-06 0:17 
GeneralRe: Excellent article...YET A simple way to implement properties.... PinmemberAnandChavali21-Dec-06 0:18 
GeneralRe: Excellent article...YET A simple way to implement properties.... PinmemberJason King11-Jan-07 7:36 
GeneralProperties by preprocessing PinmemberBoraski14-Jul-03 22:42 
GeneralRe: Properties by preprocessing PinmemberDragon Lord24-Jul-03 16:33 
GeneralRe: Properties by preprocessing PinmemberBoraski27-Jul-03 20:35 
I agree pretty much with your reply. I was just presenting an idea to see whether it made sense. I thought this way the property mechanism would affect the build process and not the runtime behavior.
 
And yes, a C++ parser is definitely not a simple thing to make.

GeneralA small bug in DECLARE_PROPERTY() PinmemberAnonymous19-Jun-02 2:59 
Questionand macros? PinmemberAnonymous10-Jun-02 21:13 
AnswerRe: and macros? PinmemberJason King8-Jul-02 12:41 
GeneralGet rid of the pThis pointer PinmemberMatthias Mann2-Jun-02 6:53 
GeneralRe: Get rid of the pThis pointer PinmemberJim Crafton2-Jun-02 7:17 
GeneralRe: Get rid of the pThis pointer PinmemberMatthias Mann2-Jun-02 8:23 
GeneralRe: Get rid of the pThis pointer PinmemberJim Crafton3-Jun-02 2:50 
GeneralRe: Get rid of the pThis pointer PinmemberJason King2-Jun-02 10:20 
GeneralExcellent! PinmemberJörgen Sigvardsson2-Jun-02 5:37 
GeneralWrong rating!!! <--- the mathematics I mean PinmemberNish - Native CPian2-Jun-02 0:15 
GeneralRe: Wrong rating!!! <--- the mathematics I mean PinmemberJörgen Sigvardsson2-Jun-02 5:36 
GeneralRe: Wrong rating!!! <--- the mathematics I mean PinmemberC#2-Jun-02 5:44 
GeneralRe: Wrong rating!!! <--- the mathematics I mean PinmemberJörgen Sigvardsson2-Jun-02 6:06 

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

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

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