Сообщение об ошибке немного странное, и я полагаю, что оно связано с разницей между Visual Studio и CLANG при обработке исходного кода.
У меня есть удобный компилятор Visual Studio 2005, и у меня есть приложение MFC, над которым я работаю, поэтому исходный код MFC для Visual Studio 2005 удобен. Я быстро взглянул на 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
— это typedef для класса, указанного в директиве 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<>
— отлавливать ошибки программиста из-за дефектного интерфейса, выдавая ошибку компилятора, если интерфейс метода отличается от ожидаемого.
person
Richard Chambers
schedule
16.07.2018