Click here to Skip to main content
15,869,940 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 141K   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

 
GeneralRe: Wrong rating!!! <--- the mathematics I mean Pin
Nish Nishant2-Jun-02 15:21
sitebuilderNish Nishant2-Jun-02 15:21 
GeneralRe: Wrong rating!!! <--- the mathematics I mean Pin
Jörgen Sigvardsson2-Jun-02 21:33
Jörgen Sigvardsson2-Jun-02 21:33 
GeneralRe: Wrong rating!!! <--- the mathematics I mean Pin
Nish Nishant2-Jun-02 21:45
sitebuilderNish Nishant2-Jun-02 21:45 
GeneralRe: Wrong rating!!! <--- the mathematics I mean Pin
Rama Krishna Vavilala3-Jun-02 7:41
Rama Krishna Vavilala3-Jun-02 7:41 
GeneralRe: Wrong rating!!! <--- the mathematics I mean Pin
Nish Nishant3-Jun-02 8:02
sitebuilderNish Nishant3-Jun-02 8:02 
GeneralRe: Wrong rating!!! <--- the mathematics I mean Pin
2-Jun-02 8:43
suss2-Jun-02 8:43 
GeneralRe: Wrong rating!!! <--- the mathematics I mean Pin
Nish Nishant2-Jun-02 15:25
sitebuilderNish Nishant2-Jun-02 15:25 

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.