错误消息有点奇怪,我想它与 Visual Studio 和 CLANG 之间在处理源代码方面的差异有关。
我使用的编译器是 Visual Studio 2005,我正在开发一个 MFC 应用程序,因此 Visual Studio 2005 的 MFC 源代码很方便。我用相同的解决方案快速浏览了 Visual Studio 2015,看起来 MFC 头文件是相似的。所以我将基于 Visual Studio 2005 MFC。
ON_COMMAND()
afxmsg_.h 中的宏定义如下:
#define ON_COMMAND(id, memberFxn) \
{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, \
static_cast<AFX_PMSG> (memberFxn) },
// ON_COMMAND(id, OnBar) is the same as
// ON_CONTROL(0, id, OnBar) or ON_BN_CLICKED(0, id, OnBar)
并AFX_PMSG
在文件 afxwin.h 中定义为:
// pointer to afx_msg member function
#ifndef AFX_MSG_CALL
#define AFX_MSG_CALL
#endif
typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);
该类CCmdTarget
是一个基类,从它派生出其他类,例如CWnd
和CWinThread
其他使用消息映射的 MFC 类。
所以ON_COMMAND()
宏正在使用static_cast<>
应该是窗口或线程目标的基类。也许其他更有知识的人可以提供关于编译器在做什么以及 C++ 语言规范将如何处理此构造的实际解释。
但是,在更实际的情况下,我建议您编写自己的ON_COMMAND()
宏版本并将此版本插入到解决方案的每个项目中的 StdAfx.h 文件中。我选择了 StdAfx.h 文件,因为每个项目只有一个文件,而且它是单个修改可以影响多个编译单元的中心点。
在所有各种包含之后的文件底部和#endif
关闭已经包含的头文件的测试之前,添加以下源代码行。
#undef ON_COMMAND
#define ON_COMMAND(id, memberFxn) \
{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, \
static_cast<AFX_PMSG> (&ThisClass :: memberFxn) },
// ON_COMMAND(id, OnBar) is the same as
// ON_CONTROL(0, id, OnBar) or ON_BN_CLICKED(0, id, OnBar)
这有两件事。
首先,它取消定义ON_COMMAND()
宏的当前定义,以便您可以将其替换为您自己的。
其次,它对方法指针使用类方法成员符号。我无法使用 CLANG 进行测试,但是它应该进行与您所说的手动操作相同的源文本替换。
ON_COMMAND(CID_ButtonAction, &SomeForm::OnButtonAction)
ThisClass
是BEGIN_MESSAGE_MAP()
指令中指定的类的类型定义(例如BEGIN_MESSAGE_MAP(CFrameworkWnd, CWin)
),由BEGIN_MESSAGE_MAP()
宏生成,如下所示:
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
PTM_WARNING_DISABLE \
const AFX_MSGMAP* theClass::GetMessageMap() const \
{ return GetThisMessageMap(); } \
const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
{ \
typedef theClass ThisClass; \
typedef baseClass TheBaseClass; \
static const AFX_MSGMAP_ENTRY _messageEntries[] = \
{
我用 Visual Studio 测试了这种方法,一切都编译得很好,它适用于 Visual Studio 2005。
请注意,可能还有其他消息映射宏可能需要类似的解决方法,因为static_cast<AFX_PMSG>
在大多数消息映射宏中使用似乎很常见。
一个奇怪的区别
对此,afxmsg_.h 中各种宏的一个奇怪区别是使用类方法指针表示法的一整套宏。一个例子如下:
#define ON_WM_PAINT() \
{ WM_PAINT, 0, 0, 0, AfxSig_vv, \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< void (AFX_MSG_CALL CWnd::*)(void) > ( &ThisClass :: OnPaint)) },
查看一些特定的事件宏,它们似乎重用了ON_CONTROL()
宏,因此除了宏之外替换该ON_COMMAND()
宏会在控制特定 MFC 宏的集合中产生涟漪效应。
// Combo Box Notification Codes
#define ON_CBN_ERRSPACE(id, memberFxn) \
ON_CONTROL(CBN_ERRSPACE, id, memberFxn)
总结
使用这种用您自己的版本覆盖默认宏的方法,包含文件 afxmsg_.h 似乎包含需要更改的内容的列表。似乎有两组 MFC 宏需要替换版本,文件顶部附近(以 开头ON_COMMAND()
)和包含文件 afxmsg_.h 底部附近的一些宏。
例如,ON_MESSAGE()
宏需要更改为:
// for Windows messages
#define ON_MESSAGE(message, memberFxn) \
{ message, 0, 0, 0, AfxSig_lwl, \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > \
(&ThisClass :: memberFxn)) },
我想知道为什么会有多种样式(可能是由于多年来不同的人添加了新的宏而没有费心改变现有的宏?)。我很好奇为什么在过去的二十年中这个问题没有得到解决,因为 MFC 至少可以追溯到 Visual Studio 6.x,并且有机会使宏统一。例如,Visual Studio 2005 的发布将是一个好时机。也许有人担心与庞大的 Visual Studio 6.x MFC 代码库的向后兼容性?
现在我知道为什么要量身定做、具体的static_cast<>
. 它允许检测具有错误或不匹配接口签名的类方法以及编译错误。因此,C 风格的强制转换是通过函数指针的定义来纠正错误,AFX_MSGMAP_ENTRY
并且static_cast<>
如果方法接口与预期的不同,则通过发出编译器错误来捕获由于接口缺陷导致的程序员错误。