Problem with BEGIN_TEMPLATE_MESSAGE_MAP (long)

S

skydivergm

Hi,

Consider the following code:

---

template <class T>
class TMyCWndWrapper : public T
{
public:
afx_msg void OnEnable(BOOL bEnable)
{
// For the sake of simplicity I'll just call the base class imp.
T::OnEnable(bEnable);
}

DECLARE_MESSAGE_MAP()
};

// Define a custom template IMPLEMENT_MESSAGE_MAP
#define WND_TEMPLATE_IMPLEMENT_MESSAGE_MAP(baseClass) \
BEGIN_MESSAGE_MAP(TMyCWndWrapper< baseClass >, baseClass) \
ON_WM_ENABLE() \
END_MESSAGE_MAP()


WND_TEMPLATE_IMPLEMENT_MESSAGE_MAP( CListBox );
WND_TEMPLATE_IMPLEMENT_MESSAGE_MAP( CListCtrl );

---

This code compiles just fine in VC6.
In VC8, I get the following error:
error C2906: 'const AFX_MSGMAP
*TMyCWndWrapper<T>::GetThisMessageMap(void)' : explicit specialization
requires 'template <>'

So I went on and implemented my own BEGIN_TEMPLATE_MESSAGE_MAP and
added the "template <>" where needed.

When I compiled, I had a macro redefinition on
BEGIN_TEMPLATE_MESSAGE_MAP. It turns out that MFC already took care of
that, so I removed my implementation and used MFC's with the adjusted
line:
BEGIN_TEMPLATE_MESSAGE_MAP(TMyCWndWrapper, baseClass , baseClass)

Now, if I define only the first line:
WND_TEMPLATE_IMPLEMENT_MESSAGE_MAP( CListBox );

The code actually compiles, but if I add the other line:
WND_TEMPLATE_IMPLEMENT_MESSAGE_MAP( CListCtrl );

Now I get the error:
error C2995: 'const AFX_MSGMAP *TMyCWndWrapper<T>::GetMessageMap(void)
const' : function template has already been defined.


My current status is that I re-implemented BEGIN_TEMPLATE_MESSAGE_MAP
as follows:

---
#undef BEGIN_TEMPLATE_MESSAGE_MAP
#define BEGIN_TEMPLATE_MESSAGE_MAP(theClass, type_name, baseClass) \
PTM_WARNING_DISABLE \
template <> \
const AFX_MSGMAP* theClass< type_name >::GetMessageMap() const \
{ return GetThisMessageMap(); } \
template <> \
const AFX_MSGMAP* PASCAL theClass< type_name >::GetThisMessageMap() \
{ \
typedef theClass< type_name > ThisClass; \
typedef baseClass TheBaseClass; \
static const AFX_MSGMAP_ENTRY _messageEntries[] = \
{
---

The diff. between this one and MFC's is that I replaced:
template < typename type_name >
with:
template <>

---

The code now compiles just fine.
So, in case this helps someone, I'll be very happy.
Beyond that, if someone would like to explain what's going on here I
will be happy to receive an answer.
I don't understand what "template < typename type_name >" means.

Cheers.
 
B

Ben Voigt

---
#undef BEGIN_TEMPLATE_MESSAGE_MAP
#define BEGIN_TEMPLATE_MESSAGE_MAP(theClass, type_name, baseClass) \
PTM_WARNING_DISABLE \
template <> \
const AFX_MSGMAP* theClass< type_name >::GetMessageMap() const \
{ return GetThisMessageMap(); } \
template <> \
const AFX_MSGMAP* PASCAL theClass< type_name >::GetThisMessageMap() \
{ \
typedef theClass< type_name > ThisClass; \
typedef baseClass TheBaseClass; \
static const AFX_MSGMAP_ENTRY _messageEntries[] = \
{
---

The diff. between this one and MFC's is that I replaced:
template < typename type_name >
with:
template <>

---

The code now compiles just fine.
So, in case this helps someone, I'll be very happy.
Beyond that, if someone would like to explain what's going on here I
will be happy to receive an answer.
I don't understand what "template < typename type_name >" means.

The difference is that your code implements a single specialized message map
each time, whereas the library code declares an entire family of message
maps using a template parameter. With the library version, you aren't
actually specifying what the base class is... instead you are giving a name
to the base class which can be used inside the message map to declare
functions that use the base class, whatever it may be, in the argument list
or return type.
 
G

Guest

Ben said:
The difference is that your code implements a single specialized message map
each time, whereas the library code declares an entire family of message
maps using a template parameter. With the library version, you aren't
actually specifying what the base class is... instead you are giving a name
to the base class which can be used inside the message map to declare
functions that use the base class, whatever it may be, in the argument list
or return type.

I'm doing my best to understand what you wrote, but I'm having a hard
time connecting it to real life.
Can you please give a usage example to MFC's version of the macro? I'm
just missing its purpose.
Also, am I on the right track, or do you have a better idea for me to
solve my issue using standard macros instead of redefining it?

TIA.
 
B

Ben Voigt

I'm doing my best to understand what you wrote, but I'm having a hard
time connecting it to real life.
Can you please give a usage example to MFC's version of the macro? I'm
just missing its purpose.
Also, am I on the right track, or do you have a better idea for me to
solve my issue using standard macros instead of redefining it?

You have something along these lines you wrote:

// Define a custom template IMPLEMENT_MESSAGE_MAP
#define WND_TEMPLATE_IMPLEMENT_MESSAGE_MAP(baseClass) \
BEGIN_MESSAGE_MAP(TMyCWndWrapper< baseClass >, baseClass) \
ON_WM_ENABLE() \
END_MESSAGE_MAP()


WND_TEMPLATE_IMPLEMENT_MESSAGE_MAP( CListBox );
WND_TEMPLATE_IMPLEMENT_MESSAGE_MAP( CListCtrl );


With the MFC map, you get the per-class repetition for free as part of the
template behavior, no macro needed.

BEGIN_TEMPLATED_MESSAGE_MAP(TMyCWndWrapper, baseClass, baseClass) \
ON_WM_ENABLE()
END_MESSAGE_MAP()

defines the message map for TMyCWndWrapper<baseClass> for all needed values
of baseClass. The compiler will instantiate the template for both
TMyCWndWrapper<CListBox> and TMyCWndWrapper<CListCtrl> if you have used them
anywhere. This is far better than the macro because: it is type-safe and
error reporting is far better, and there is no need to maintain the list of
instances.

or to see why there need to be three arguments instead of two, add to the
above:
template <typename T>
class TMyDerivedWnd : TMyCWndWrapper<T> {};

BEGIN_TEMPLATED_MESSAGE_MAP(TMyDerivedWnd, baseClass,
TMyCWndWrapper<baseClass>) \
ON_WM_ENABLE()
END_MESSAGE_MAP()
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top