简体   繁体   English

什么规则管理参数默认分配?

[英]What rules govern argument default assignment?

I have a set of exception classes for use on Win32API and MFC which capture the current Win32 error code ( GetLastError() ) and formulate a human readable message about where it occurred.我有一组用于 Win32API 和 MFC 的异常类,它们捕获当前的 Win32 错误代码( GetLastError() )并制定有关它发生位置的人类可读消息。

I rely upon the ability to capture the current error code before the ctor begin doing their work, under the assumption that surely the arguments to the ctor must be resolved before the ctor is invoked.我依赖于在 ctor 开始工作之前捕获当前错误代码的能力,假设必须在调用 ctor 之前解析 ctor 的参数。

But I'm running into issues in my current build, having switched compilation tooling from 120_xp to 120 in VS2013 (I am not 100% certain that this is the source of the change - it may have lain dormant for some time unrelated to the platform toolset change).但是我在当前版本中遇到了问题,在 VS2013 中将编译工具从 120_xp 切换到 120(我不能 100% 确定这是更改的来源 - 它可能已经休眠了一段时间,与平台无关工具集更改)。

However, I would have thought that none of that is relevant - that C++ would require that all arguments are resolved first, then the function call is executed, so that the default argument error below would always have the current error code before invoking the ctor (which could potentially change it):但是,我会认为这些都无关紧要 - 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)
{

}

Here's the calling context:这是调用上下文:

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());

If I change the calling context to force the capture of the current error code, I do in fact get error 2:如果我更改调用上下文以强制捕获当前错误代码,我实际上会得到错误 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 is simple, and it preserves the current error code: 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;
}

The problem I'm experiencing is that I'm seeing error 122: buffer supplied to system call is too small.我遇到的问题是我看到错误 122:提供给系统调用的缓冲区太小。 But the error code at the point of invoking this exception is error 2: file not found.但是调用这个异常时的错误代码是错误2:找不到文件。

I would far prefer that my software report "file not found" which is more correct and actionable.我更希望我的软件报告“找不到文件”更正确和可操作。

So: 1. Am I wrong about C++ guaranteeing that all arguments are resolved before the function call (the ctor) is invoked?所以: 1. C++ 保证在调用函数调用(构造函数)之前解析所有参数,我错了吗? 2. Where else could calls be getting made that could change the current error code (CString ctor(s) are the only other things being invoked before the CWin32APIErrorException::CWin32APIErrorException() ctor. 2. 还有哪些地方可以调用可以改变当前的错误代码(CString ctor(s) 是在CWin32APIErrorException::CWin32APIErrorException() ctor 之前被调用的唯一其他东西。

Meaning that CString ctor is apparently changing the current error?!这意味着 CString ctor 显然正在改变当前的错误?! ugh!啊!

Can someone let me know where I'm wrong in my understanding?有人可以让我知道我的理解错在哪里吗?

Thank you!谢谢!

I'm going to leave this here in case it helps others with a similar issue:我将把它留在这里,以防它帮助其他有类似问题的人:

In my case above, I was compiling UNICODE, and my CWin32APIErrorException::CWin32APIErrorException(const CString & source, ... was being called using throw CWin32APIErrorException(__FUNCTION__, ... which was then calling CStringW(const char *) constructor, which internally calls MultiByteToWideChar() which uses the pattern of call first time with null buffer to get the size of buffer required, then call a second time with the buffer allocated to that size.在我上面的例子中,我正在编译 UNICODE,我的CWin32APIErrorException::CWin32APIErrorException(const CString & source, ...被调用使用throw CWin32APIErrorException(__FUNCTION__, ...然后调用CStringW(const char *)构造函数,它内部调用MultiByteToWideChar() ,它使用第一次调用模式与空缓冲区来获取所需的缓冲区大小,然后使用分配给该大小的缓冲区第二次调用。

The first call thus always sets the current error to 122: buffer too small.因此,第一次调用总是将当前错误设置为 122:缓冲区太小。

Hence the behavior I was seeing.因此,我看到的行为。

We did not see this problem when compiling for _MBCS , since the __FUNCTION__ literal was not being converted internally - the CStringA(const char *) was not overwriting the current error code because no conversions were necessary.我们在为_MBCS编译时没有看到这个问题,因为__FUNCTION__文字没有在内部转换 - CStringA(const char *)没有覆盖当前的错误代码,因为不需要转换。

It could be that at any time in the future CString constructor could arbitrarily have the side effect of changing the current error for any other set of reasons... so having CWin32APIErrorException take any sort of constructed object type means that it inherently is prone to getting the wrong error code unless the caller makes sure to capture it first and pass on the captured value:可能在未来的任何时间CString构造函数都可能出于任何其他原因而任意更改当前错误的副作用......因此让CWin32APIErrorException采用任何类型的构造对象类型意味着它本质上很容易获得错误的错误代码,除非调用者确保首先捕获它并传递捕获的值:

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

It's possible to change the CWin32APIErrorException ctors to take const char * and separately const wchar_t * to avoid having the CString construction take place before capturing the current error code.可以将CWin32APIErrorException更改为采用const char *和单独的const wchar_t *以避免在捕获当前错误代码之前进行 CString 构造。

The problem with that approach is that the permutations quickly add up (narrow, narrow), (narrow, wide), (wide, narrow), (wide, wide).这种方法的问题在于排列很快就会加起来(窄,窄),(窄,宽),(宽,窄),(宽,宽)。 And that's just for two arguments.这只是两个论点。 My actual class has three (one optional one for the arguments).我的实际课程有三个(一个可选的参数)。

And that last one is best left as a CString, because it is very likely to be dynamically created at runtime, not known at compile time, unlike the first two arguments.最后一个最好保留为 CString,因为它很可能在运行时动态创建,在编译时不知道,与前两个参数不同。 So it will very likely incur the possibility of changing the current error code, even if the ctor interface requires a simple char* or wchar_t*... which means that this problem is not solved.所以它很可能会招致改变当前错误代码的可能性,即使ctor接口需要一个简单的char*或wchar_t*...这意味着这个问题没有解决。

Ultimately, I was unable to come up with a way to capture the current error before capturing the other arguments needed for the error report in a way that is guaranteed to work - so I just changed the interface to use explicit arguments (no default values) to force me to go through all of our code and make sure that we're capturing the correct error code at the call site and passing that on.最终,在以保证有效的方式捕获错误报告所需的其他参数之前,我无法想出一种方法来捕获当前错误 - 所以我只是将接口更改为使用显式参数(无默认值)强迫我检查我们所有的代码并确保我们在调用站点捕获正确的错误代码并将其传递。

For example:例如:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 哪些规范规则管理和允许 SFINAE? - What normative rules govern and allow SFINAE? 什么规则管理类型之间多个用户定义的转换的使用? - What rules govern use of multiple user-defined conversions between types? 自动参数捕获的演绎规则是什么? - What are the deduction rules for automatic argument capture? 默认定义的移动构造函数的noexcept规则是什么? - What are the rules for noexcept on default defined move constructors? 在模板参数中,哪些规则允许编译器推断数组的项数? - In a template argument, what rules allow the compiler to infer the number of items of an array? 将指针传递给模板参数时类型推导的规则是什么 - What is the rules of type deduction while passing pointer to template argument 默认生成的副本构造函数为常量表达式的规则是什么? - What are the rules for default generated copy constructors being constant expressions? 将向量传递给函数时,默认参数(如果有)应该是什么? - What should the default argument(if any) be while passing a vector to a function? 临时绑定到引用参数的默认参数的生命周期是多少? - What is the lifetime of a default argument temporary bound to a reference parameter? 默认参数,如果默认可构造 - Default argument if default constructible
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM