Visual Studio 2008 c++条件模板继承错误?
我正在将 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 中,调用原始 CWindow 类的方法,而不是 CFakeWindow 类的方法。这当然会导致测试失败,因为 CWindow
中散布着 ATLASSERT(::IsWindow(m_hWnd))
。
当我单步执行调试器中的代码时,我看到 CAboutDialog 类继承自 CDialogImpl
。然而,当我调用CAboutDialog
上的方法(例如EndDialog(code)
)时,CWindow
方法就会被调用。
这是 VS 2008 中的错误,还是我的条件模板继承技术是 VS 2005 允许但 VS 2008“修复”的令人厌恶的技术?是否有解决方法,或者我是否需要考虑不同的技术来对 WTL 类进行单元测试?我真的很喜欢这种技术,因为它让我可以将 WTL 类放入测试工具中,而无需浪费时间使用 WTL 库。
编辑:正如下面对 Conal 的响应所述,预处理器输出显示我的类继承自 CFakeWindow:
class CAboutDialog :
public CDialogImpl<CAboutDialog, CFakeWindow >
, public CDialogResize< CAboutDialog >
...
如上所述,当我在调试器中单步执行代码时,CAboutDialog 显示在本地窗口继承自 CFakeWindow。
编辑2:根据Conal的建议,我逐步进行了反汇编,代码应该调用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 调试器中的错误。
I'm in the process of porting a C++/WTL project from Visual Studio 2005 to VS 2008. One of the project configurations is a unit-testing build, which defines the preprocessor symbol UNIT_TEST.
In order to get my WTL classes into my test harness, I made a CFakeWindow
class that stubs all the CWindow methods. Then in my stdafx.h file, I do this right below the import of atlwin.h (which defines the CWindow class):
#ifdef UNIT_TEST
#include "fakewindow.h"
#define CWindow CFakeWindow
#endif
My window classes then look like this:
class CAboutDialog :
public CDialogImpl< CAboutDialog, CWindow >
, public CDialogResize< CAboutDialog >
{
// class definition omitted...
};
This works great in VS 2005. The problem is that in VS 2008, the methods from the original CWindow class are getting called, instead of the CFakeWindow class. This of course causes the tests to fail, because CWindow
is sprinkled with ATLASSERT(::IsWindow(m_hWnd))
.
When I step through the code in the debugger, I see that the CAboutDialog class is inheriting from CDialogImpl<CAboutDialog, CFakeWindow>
. Yet when I call a method on CAboutDialog
(e.g. EndDialog(code)
), the CWindow
method is getting called.
Is this a bug in VS 2008, or was my conditional template inheritance technique an abomination that VS 2005 allowed but VS 2008 "fixed"? Is there a work-around, or do I need to consider a different technique to unit-test WTL classes? I really like this technique, because it lets me get WTL classes into a test harness without mucking about with the WTL library.
Edit: As noted in the response to Conal, below, the preprocessor output shows that my class is inheriting from CFakeWindow:
class CAboutDialog :
public CDialogImpl<CAboutDialog, CFakeWindow >
, public CDialogResize< CAboutDialog >
...
And as stated above, when I step through the code in the debugger, CAboutDialog is shown in the locals window as inheriting from CFakeWindow.
Edit 2: As per Conal's advice, I stepped through the disassembly, and the code is supposedly calling the CFakeWindow method, but the CWindow method is what is actually called.
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)
I'm starting to lean more toward a bug in the VC++ 2008 debugger.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
这可能是一种更清晰的处理方式:
也许存在重新定义未通过预编译头的情况。如果是这样,这将捕获任何此类问题。
This might be a clearer way of handling it:
Perhaps there's a case where the redefine is not getting through the precompiled headers. If so, this will catch any such problem.
我认为这是一个不太可能的事情,但是预编译头文件是否有可能对您的构建造成严重破坏?尝试在项目中关闭它们(如果它们打开)并进行干净的构建,看看是否有更好的行为。
I think this is a long shot, but is it possible that precompiled headers are playing havoc with your build? Try turning them off in your project (if they're on) and do a clean build to see if you get any better behavior.
您是否查看过预处理器输出以确认您的黑客实际上正在按照您的想法进行操作?使用预处理器重命名模板库中的类充满了危险。还要检查 CFakeWindow 中每个方法的签名是否与 CWindow 完全匹配。
VS2008 已经存在很长一段时间了,所以在您首先消除所有其他可能性之前,我不会考虑编译器错误。
Have you looked at the preprocessor output to confirm that your hack is in fact doing what you think? Using the preprocessor to rename classes that are part of a template library is fraught with danger. Also check that the signature of each and every method in your CFakeWindow exactly matches CWindow.
VS2008 has been around for quite a long time now, so I would not be considering a compiler bug until you have first eliminated all other possibilities.
尝试类似的方法:
这样您就可以停止尝试使用预处理器重写系统头文件,并将其用于您自己的代码。
是的,我知道你必须“重构”你的代码,但这是一项搜索和替换工作。
您当前的技术有点像黑客,因为它做出了一个没有根据的假设:没有源文件会 #include 来自 ATL 的任何依赖于 CWindow 一致定义的标头。这不太可能稳定,因为代码会随着时间的推移而变化。
Try something like:
That way you can stop trying to use the preprocessor to rewrite system header files, and use it for your own code.
Yes, I know you have to "refactor" your code, but it's a search-and-replace job.
Your current technique is something of a hack because it makes an unwarranted assumption: that no source file will #include any header from ATL that relies on a consistent definition of CWindow. This is not likely to be stable as code changes over time.
操作,stdafx.h 默认情况下是预编译头。因此,当您定义
UNIT_TEST
宏时,它不会影响预编译文件,该文件仍然以CWindow
作为模板参数。AFAIK, stdafx.h is a precompiled header by default. So when you define
UNIT_TEST
macro, it doesn't affect precompiled file, which still hasCWindow
as template argument.我看到库符号位于命名空间 ATL 中。这是否可能会摆脱你的宏观诡计?
一般来说,我不会指望任何调试器能够完美地报告模板实例化。即使在调试版本中也需要发生太多奇怪的优化。
I see the library symbols are in the namespace ATL. Is that perhaps throwing off your macro trickery?
In general, I would not count on any debugger to report template instantiations perfectly. Too many weird optimizations are required to happen even in Debug builds.