Ошибка условного наследования шаблонов в Visual Studio 2008 c ++? - PullRequest
1 голос
/ 25 октября 2009

Я в процессе переноса проекта C ++ / WTL из Visual Studio 2005 в VS 2008. Одна из конфигураций проекта - это сборка для модульного тестирования, которая определяет символ препроцессора UNIT_TEST.

Чтобы включить мои классы WTL в свой тестовый комплект, я создал класс CFakeWindow, который заглушает все методы CWindow. Затем в моем файле stdafx.h я делаю это прямо под импортом atlwin.h (который определяет класс CWindow):

#ifdef UNIT_TEST
#include "fakewindow.h"
#define CWindow CFakeWindow
#endif

Мои классы окон выглядят так:

class CAboutDialog : 
    public CDialogImpl< CAboutDialog, CWindow > 
    , public CDialogResize< CAboutDialog >
{
// class definition omitted...
};

Это прекрасно работает в VS 2005. Проблема в том, что в VS 2008 вместо класса CFakeWindow вызываются методы из исходного класса CWindow. Это, конечно, приводит к сбою тестов, потому что CWindow обрызгивается ATLASSERT(::IsWindow(m_hWnd)).

Когда я выполняю код в отладчике, я вижу, что класс CAboutDialog наследуется от CDialogImpl<CAboutDialog, CFakeWindow>. Тем не менее, когда я вызываю метод на CAboutDialog (например, EndDialog(code)), вызывается метод CWindow.

Это ошибка в VS 2008, или моя техника условного наследования шаблонов была мерзостью, которую допускал VS 2005, но VS 2008 "исправил"? Есть ли обходной путь, или мне нужно рассмотреть другую технику для модульного тестирования классов WTL? Мне очень нравится этот метод, потому что он позволяет мне помещать WTL-классы в тестовый комплект без использования библиотеки WTL.

Редактировать : Как отмечено в ответе Конала ниже, выходные данные препроцессора показывают, что мой класс наследуется от CFakeWindow:

class CAboutDialog :
    public CDialogImpl<CAboutDialog, CFakeWindow >
    , public CDialogResize< CAboutDialog >
    ...

И, как указано выше, когда я выполняю код в отладчике, CAboutDialog отображается в окне локальных компьютеров как наследник от CFakeWindow.

Редактировать 2 : Согласно совету Конала, я прошел через разборку, и код предположительно вызывает метод CFakeWindow, но метод CWindow - это то, что на самом деле называется.

        if ( wID == IDCANCEL ) 
00434898  movzx       edx,word ptr [ebp+8] 
0043489C  cmp         edx,2 
0043489F  jne         CAboutDialog::OnCloseCmd+90h (4348B0h) 
        {
            EndDialog( wID ) ;
004348A1  movzx       eax,word ptr [ebp+8] 
004348A5  push        eax  
004348A6  mov         ecx,dword ptr [ebp-10h] 
004348A9  call        ATL::CDialogImpl<CAboutDialog,ATL::CFakeWindow>::EndDialog (40D102h) 
        }
        else
004348AE  jmp         CAboutDialog::OnCloseCmd+9Ah (4348BAh) 
        {
            EndDialog(IDOK);
004348B0  push        1    
004348B2  mov         ecx,dword ptr [ebp-10h] 
004348B5  call        ATL::CDialogImpl<CAboutDialog,ATL::CFakeWindow>::EndDialog (40D102h) 

Я начинаю больше склоняться к ошибке в отладчике VC ++ 2008.

Ответы [ 6 ]

4 голосов
/ 11 ноября 2009

Это может быть более понятный способ обработки:

#ifdef UNIT_TEST
#include "fakewindow.h"
#define TWindow CFakeWindow
#else
#define TWindow CWindow
#endif

Возможно, есть случай, когда переопределение не проходит через предварительно скомпилированные заголовки. Если так, это поймает любую такую ​​проблему.

2 голосов
/ 10 ноября 2009

Я думаю, что это длинный выстрел, но возможно ли, что скомпилированные заголовки разрушают вашу сборку?Попробуйте отключить их в своем проекте (если они включены) и выполните чистую сборку, чтобы увидеть, получится ли у вас какое-либо поведение лучше.

1 голос
/ 12 ноября 2009

Я вижу, что символы библиотеки находятся в пространстве имен ATL. Возможно, это сбрасывает твой трюк с макросами?

В общем, я бы не стал рассчитывать на какой-либо отладчик для идеального отчета об экземплярах шаблона Слишком много странных оптимизаций требуется даже в сборках отладки.

1 голос
/ 11 ноября 2009

Затем в моем файле stdafx.h я делаю это

AFAIK, stdafx.h - это предварительно скомпилированный заголовок по умолчанию. Поэтому, когда вы определяете макрос UNIT_TEST, он не влияет на предварительно скомпилированный файл, который все еще имеет CWindow в качестве аргумента шаблона.

1 голос
/ 11 ноября 2009

Попробуйте что-то вроде:

class CAboutDialog :
    public CDialogImpl<CAboutDialog, FAKE_CWindow_MACRO >

Таким образом, вы можете прекратить попытки использовать препроцессор для перезаписи системных заголовочных файлов и использовать его для собственного кода.

Да, я знаю, что вы должны «рефакторировать» свой код, но это работа поиска и замены.

Ваша текущая техника является чем-то вроде хака, потому что она делает необоснованное предположение: ни один исходный файл не будет включать в себя # заголовок из ATL, который опирается на непротиворечивое определение CWindow. Это вряд ли будет стабильным, так как код меняется со временем.

1 голос
/ 25 октября 2009

Вы смотрели на вывод препроцессора, чтобы подтвердить, что ваш хак на самом деле делает то, что вы думаете? Использование препроцессора для переименования классов, являющихся частью библиотеки шаблонов, чревато опасностью. Также убедитесь, что сигнатура каждого метода в вашем CFakeWindow точно соответствует CWindow.

VS2008 существует довольно давно, поэтому я не буду рассматривать ошибку компилятора, пока вы сначала не исключите все другие возможности.

...