簡體   English   中英

什么規則管理參數默認分配?

[英]What rules govern argument default assignment?

我有一組用於 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++ 保證在調用函數調用(構造函數)之前解析所有參數,我錯了嗎? 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);
}

可以將CWin32APIErrorException更改為采用const char *和單獨的const wchar_t *以避免在捕獲當前錯誤代碼之前進行 CString 構造。

這種方法的問題在於排列很快就會加起來(窄,窄),(窄,寬),(寬,窄),(寬,寬)。 這只是兩個論點。 我的實際課程有三個(一個可選的參數)。

最后一個最好保留為 CString,因為它很可能在運行時動態創建,在編譯時不知道,與前兩個參數不同。 所以它很可能會招致改變當前錯誤代碼的可能性,即使ctor接口需要一個簡單的char*或wchar_t*...這意味着這個問題沒有解決。

最終,在以保證有效的方式捕獲錯誤報告所需的其他參數之前,我無法想出一種方法來捕獲當前錯誤 - 所以我只是將接口更改為使用顯式參數(無默認值)強迫我檢查我們所有的代碼並確保我們在調用站點捕獲正確的錯誤代碼並將其傳遞。

例如:

    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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM