zar*_*zar 19 c++ winapi mfc design-patterns coding-style
Windows API使用GetLastError()机制来检索有关错误或失败的信息.我正在考虑使用相同的机制来处理错误,因为我正在为专有模块编写API.我的问题是API更好地直接返回错误代码?不会GetLastError()有任何特别的优势?考虑下面的简单Win32 API示例:
HANDLE hFile = CreateFile(sFile,
GENERIC_WRITE, FILE_SHARE_READ,
NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
DWORD lrc = GetLastError();
if (lrc == ERROR_FILE_EXISTS)
{
// msg box and so on
}
}
Run Code Online (Sandbox Code Playgroud)
当我编写自己的API时,我意识到GetLastError()机制意味着CreateFile()必须在所有出口点设置最后一个错误代码.如果有许多退出点并且其中一个可能错过,则这可能会有一点容易出错.愚蠢的问题,但这是如何完成或有某种设计模式呢?
另一种方法是为函数提供一个额外的参数,它可以直接填写错误代码,因此GetLastError()不需要单独的调用.另一种方法可以如下.我将坚持使用上面的Win32 API,这是分析器的好例子.在这里,我将格式更改为此(假设).
result = CreateFile(hFile, sFile,
GENERIC_WRITE, FILE_SHARE_READ,
NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
if (result == SUCCESS)
{
// hFile has correct value, process it
}
else if (result == FILE_ALREADY_EXIT )
{
// display message accordingly
return;
}
else if ( result == INVALID_PATH )
{
// display message accordingly.
return;
}
Run Code Online (Sandbox Code Playgroud)
我的最终问题是从API或甚至只是函数返回错误代码的首选方法是什么,因为它们都是相同的?
Fre*_*abe 20
总的来说,这是一个糟糕的设计.这不是特定于Windows的GetLastError功能,Unix系统与全局errno变量具有相同的概念.这是因为它是隐含的函数的输出.这有一些令人讨厌的后果:
同时执行的两个函数(在不同的线程中)可能会覆盖全局错误代码.因此,您可能需要具有每线程错误代码.正如对这个答案的各种评论所指出的,这正是做什么GetLastError和errno做什么 - 如果你考虑为你的API使用全局错误代码,那么你需要做同样的事情,以防你的API可以从多个线程使用.
如果外部函数覆盖内部设置的错误代码,则两个嵌套函数调用可能会丢弃错误代码.
忽略错误代码非常容易.事实上,实际上很难记住它存在,因为并非每个函数都使用它.
当你自己实现一个功能时,很容易忘记设置它.可能有许多不同的代码路径,如果您不注意,其中一个可能允许控制流转义而无需正确设置全局错误代码.
通常,错误条件是例外.它们并不经常发生,但它们可以.您需要的配置文件可能无法读取 - 但大部分时间都是如此.对于此类异常错误,您应该考虑使用C++异常.任何值得它的C++书籍都会列出为什么任何语言(不仅仅是C++)中的异常都是好的原因,但在激动之前有一个重要的事情需要考虑:
例外展开堆栈.
这意味着当你有一个产生异常的函数时,它会传播给所有调用者(直到它被某人捕获,可能是C运行时系统).这反过来会产生一些后果:
所有调用者代码都需要知道异常的存在,因此所有获取资源的代码必须能够释放它们,即使面对异常(在C++中,'RAII'技术通常用于解决它们).
事件循环系统通常不允许异常转义事件处理程序.在这种情况下,处理它们没有好的概念.
处理回调的程序(例如普通函数指针,甚至是Qt库使用的'signal&slot'系统)通常不希望被调用的函数(一个槽)可以产生异常,因此它们不会打扰想抓住它.
底线是:如果您知道他们在做什么,请使用例外.因为你似乎对这个话题比较新,所以现在坚持返回函数的代码,但请记住,这通常不是一个好的技巧.在任何一种情况下都不要使用全局错误变量/函数.
GetLastError到目前为止,该模式最容易出错,而且最不受欢迎.
enum到目前为止,返回状态代码是更好的选择.
你没有提到的另一种选择,但很受欢迎,就是为失败案例抛出异常.如果你想做正确的事情(而不是泄漏资源或让对象处于半设置状态),这需要非常仔细的编码,但是会产生非常优雅的代码,其中所有核心逻辑都在一个地方并且错误处理整齐地分开了.