哪些规则控制参数默认赋值?



我有一组用于Win32API和MFC的异常类,它们捕获当前的Win32错误代码(GetLastError()),并制定有关其发生位置的人类可读消息。

我依赖于在 ctor 开始工作之前捕获当前错误代码的能力,假设在调用 ctor 之前肯定必须解决对 ctor 的参数。

但是我在当前的版本中遇到了问题,在VS2013中将编译工具从120_xp切换到120(我不是100%确定这是更改的来源-它可能已经休眠了一段时间与平台工具集更改无关)。

但是,我认为这些都不相关 - C++要求首先解析所有参数,然后执行函数调用,以便下面error默认参数在调用 ctor 之前始终具有当前错误代码(这可能会更改它):

CWin32APIErrorException::CWin32APIErrorException(const CString & source, const CString & api, DWORD error /*= GetLastError()*/) 
: CContextException(FormatString(_T("In %s, %s() returned "), source, api), error)
, m_source(source)
, m_api(api)
{
}

下面是调用上下文:

m_file = CreateFile(filename, FILE_GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (!m_file) // NOTE: m_file is smart handle and it's operator bool() knows that the invalid file handle is "false"
throw CWin32APIErrorException(__FUNCTION__, _T("CreateFile"), GetLastError());

如果我更改调用上下文以强制捕获当前错误代码,我实际上会得到错误 2:

m_file = CreateFile(filename, FILE_GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (!m_file)
{
auto error = GetLastError();
throw CWin32APIErrorException(__FUNCTION__, _T("CreateFile"), error);
}

FormatString很简单,它保留了当前的错误代码:

CStringW FormatString(const wchar_t * format, ...)
{
const DWORD dwError = ::GetLastError();
va_list args;
va_start(args, format);
CStringW str;
str.FormatV(format, args);
::SetLastError(dwError);
return str;
}

我遇到的问题是我看到错误 122:提供给系统调用的缓冲区太小。 但是调用此异常时的错误代码是错误 2:找不到文件。

我更希望我的软件报告"找不到文件",这更正确和可操作。

所以: 1. 我C++保证在调用函数调用(ctor)之前解决所有参数是错误的吗? 2. 还可以在哪里进行可能更改当前错误代码的调用(CString ctor(s) 是在CWin32APIErrorException::CWin32APIErrorException()ctor 之前调用的唯一其他内容。

意思是CString ctor显然正在更改当前错误?! 呸!

有人可以让我知道我的理解错在哪里吗?

谢谢!

我将把它留在这里,以防它帮助其他人解决类似问题:

在上面的例子中,我正在编译 UNICODE,我的CWin32APIErrorException::CWin32APIErrorException(const CString & source, ...是使用throw CWin32APIErrorException(__FUNCTION__, ...调用的,然后调用CStringW(const char *)构造函数,它在内部调用MultiByteToWideChar(),它使用空缓冲区第一次调用模式来获取所需的缓冲区大小,然后第二次调用并将缓冲区分配给该大小。

因此,第一次调用始终将当前错误设置为 122:缓冲区太小。

因此,我看到的行为。

我们在编译时没有看到这个问题_MBCS,因为__FUNCTION__文字没有在内部转换 -CStringA(const char *)没有覆盖当前的错误代码,因为不需要转换。

可能是在未来的任何时候CString构造函数可能会出于任何其他原因任意产生更改当前错误的副作用......因此,CWin32APIErrorException采用任何类型的构造对象类型意味着它本质上容易获得错误的错误代码,除非调用方确保首先捕获它并传递捕获的值:

if (!SomeWinAPIFunction(...))
{
auto error = GetLastError();  // make sure we report the correct error!
throw CWin32APIErrorException(__FUNCTION__, "SomeWinAPIFunction", error);
}

可以更改CWin32APIErrorExceptionctor 以采用const char *并单独const wchar_t *以避免在捕获当前错误代码之前进行 CString 构造。

这种方法的问题在于排列会迅速加起来(窄,窄),(窄,宽),(宽,窄),(宽,宽)。 这只是两个论点。 我的实际类有三个(一个可选的参数)。

最后一个最好保留为 CString,因为它很可能是在运行时动态创建的,在编译时不知道,这与前两个参数不同。 因此,即使 ctor 接口需要简单的字符* 或 wchar_t*,也很可能会产生更改当前错误代码的可能性......这意味着这个问题没有解决。

最终,我无法想出一种方法来捕获当前错误,然后再以保证工作的方式捕获错误报告所需的其他参数 - 所以我只是更改了接口以使用显式参数(无默认值)来强制我遍历我们所有的代码并确保我们在调用站点捕获正确的错误代码并将其传递。

例如:

if (!::DestroyAcceleratorTable(m_hAccel))
if (!m_hAccel)
{
auto error = GetLastError();
throw CWinAPIErrorException(__FUNCTION__, "DestroyAcceleratorTable", FormatString(_T("%X"), m_hAccel), error);
}

最新更新