2016-04-05 46 views
0

我有以下功能(C++):處理不正確的參數值

string dataInYear(int year) { 
    if (year < 2000) { 
    //don't support retrieving data from before 2000 
    } 
    //return data for the year >= 2000 
} 

我應該在這裏,如果一年< 2000拋出異常?怎麼樣使用輸出錯誤字符串作爲paramater:

string dataInYear(int year, string &error) { 
    if (year < 2000) { 
    error = "don't support retrieving data from before 2000"; 
    return ""; 
    } 
    //return data for the year >= 2000 
} 

我不知道如何使用異常的原因是,來電者不只是從看的頭文件,這個功能可以拋出一個異常,知道(而不僅僅是返回一個空字符串)。通過第二個選項,調用者被迫通過傳入錯誤字符串並在之後檢查來確認方法可能失敗。

或者作爲第三個選擇:

template <class A> struct Failable 
{ 
    bool error; 
    A value; 
    wxString errorString; 
    Failable(wxString errorStr) : error(true), errorString(errorStr) {} 
    Failable(A val) : error(false), value(val) {} 
    operator A() const { return value; } 
}; 

Failable<string> dataInYear(int year) { 
     if (year < 2000) { 
     error = "don't support retrieving data from before 2000"; 
     return ""; 
     } 
     //return data for the year >= 2000 
} 

//caller 

Failable<string> d = dataInYear(y); 
if (d.error) { 
    //deal with error 
} else { 
    string data = d.value; 
} 
+0

避免std :: string指針(大部分和你的用法都是錯誤的)。改爲使用參考。 –

+0

boost :: optional將是一個很好的返回值(可能在C++ 17中可用) –

+0

一個解決方案是使用*來記錄'dataInYear',除非年份<2000 *,否則調用此函數是UB。查看「按合同設計」瞭解更多詳情。有一百萬其他解決方案几乎找不到一個正確的解決方案,所以這個問題主要是基於意見的。 – nwp

回答

1

我不知道如何使用異常的原因是調用者不知道剛看的頭文件,這功能可以拋出一個異常

嗯,這是可能,使用throw關鍵字:

// Header.h 
string dataInYear(int year) throw(std::exception); 

但是,如果你覺得exception specifications are bad,那麼你可能只是評論的那部分。至少通知用戶這個功能'可以'throw
或者,您可以返回一個空字符串,因爲您已經指出了您的帖子。

auto result = dataInYear(year); 
if(result.empty()) 
    // ... error 

他們的假設沒有你喜歡的,然後有一個簡單的結構,它包含在它的錯誤代碼。

template<typename Data> 
struct Result 
{ 
    Data data; // any type can be used besides string 
    string error; 
}; 

Result<string> dataInYear(int year) 
{ 
    if(year < 2000) 
    return Result<string>{"", "don't support retrieving data from before 2000"}; 

    return Result<string>{data}; 
} 

... 

auto result = dataInYear(year); 
if(not result.error.empty()) // if `error` is filled => problem 
    // ... error 
+0

所有我聽說過的異常規範都很糟糕,所以我想我不應該這樣做。 –

+0

我會用'assert(year <2000)'來返回一個空字符串,有助於調試。 – nwp

0

另一種使用異常規範可能是這樣的:

#include <string> 

class APIExample 
{ 
    std::string m_sErrorMessage; 

public: 
    APIExample() : m_sErrorMessage("") 
    {} 

    enum class ReturnCode { 
     Ok, 
     Error, // Possible to add more codes if you'd like 
    }; 

    ReturnCode dataInYear(int nYear, std::string & sData) 
    { 
     if (nYear < 2000) { 
     m_sErrorMessage = "Year is < 2000"; 
     return ReturnCode::Error; 
     } 
     return ReturnCode::Ok; 
    } 

    ReturnCode getErrorMessage(std::string & sErrorMessage) 
    { 
     sErrorMessage = m_sErrorMessage; 

     return ReturnCode::Ok; 
    } 
}; 

int main() 
{ 

    APIExample example; 
    std::string sData; 
    if (example.dataInYear(1000, sData) != APIExample::ReturnCode::Ok) { 
     std::string sError; 
     example.getErrorMessage(sError); 
    } 
    return 0; 
} 

請注意enum class ReturnCode可以與其他錯誤/警告代碼進行擴展。

+0

C++的美麗不是例外之一嗎? – Chiel

+0

是不是'ReturnCode'有點多餘?調用者可以檢查'sErrorMessage'是否爲空。正如'm_sErrorMessage':直接寫入一個字符串引用參數,調用者可以檢查。 – Bizmarck

+0

@Chiel是的,但我認爲他正在尋找異常規範/ API的替代方案。 –

0

我不確定使用異常的原因是調用者不知道只是從頭文件中看到這個函數可以拋出異常(而不是僅僅返回一個空字符串)。

他們應該!你的文件評論在哪裏?這些應該說明的是,當先決條件侵犯的可能拋出異常。而且,無論你使用異常與否,本身應該被列說,反正記錄意見的先決條件,所以問題就變成了毫無意義:一個錯誤的參數是沒有更好的或者比例外,在這方面更糟。

同時,例外的所有其他好處來承擔(這我就不一一贅述)。

+0

好的,是的,它被記錄下來,但如果用戶沒有閱讀這個,編譯器不會警告用戶。 (我來自Swift/Java,它向編譯器和用戶顯式說明該方法拋出異常。) –

+0

@Jonathan:然後用戶疏忽了。 –