Jak: rozhraní mezi výjimečné a jiných výjimečných kód
Tento článek popisuje, jak implementovat konzistentní zpracování výjimek v modulu jazyka C++ a také jak přeložit tyto výjimky z kódů chyb hranicím výjimku a.
Někdy má modulu jazyka C++ pro rozhraní s kódem, který nepoužívá výjimky (jiných výjimečných kód).Toto rozhraní se nazývá hranice výjimka.Například můžete chtít volat funkci Win32 CreateFile v programu C++.CreateFilenelze vyvolat výjimky; Místo toho nastaví kódy chyb, které mohou být načteny pomocí GetLastError funkce.Pokud je váš program C++ netriviální, pak v ní máte pravděpodobně raději jednotné zásady založené na výjimky zpracování chyb.A pravděpodobně nechcete opustit výjimky jen proto, že rozhraní-a výjimečných kódem a ani chcete smíchat zásady bez výjimky systémy výjimku a chyba v modulu jazyka C++.
Volání jiných výjimečných funkcí z jazyka C++
Při volání jiných výjimečných funkce z jazyka C++ myšlenka je zabalit této funkce ve funkci jazyka C++, která zjistí jakékoli chyby a případně vyvolá výjimku.Při návrhu obálky funkce nejprve rozhodnout, jaký typ výjimky záruku poskytnout: Ne throw, silný nebo základní.Za druhé Navrhněte funkce tak, aby všechny prostředky, například popisovače souborů jsou správně uvolněny, pokud je vyvolána výjimka.Obvykle to znamená použít inteligentní ukazatele nebo podobných prostředků na vlastní zdroje.Další informace o navrhování, viz Jak: návrh pro bezpečnost výjimku (C++).
Příklad
Následující příklad ukazuje funkcí jazyka C++, které používají rozhraní Win32 CreateFile a ReadFile funkce interně pro otevření a čtení dva soubory.File Třída je získání prostředků je obálka inicializace (RAII) pro popisovače souborů.Jeho konstruktoru rozpozná stav "soubor nebyl nalezen" a vyvolá výjimku pro šíření chyby zásobníku volání modulu jazyka C++.Pokud po je vyvolána výjimka File objekt je zcela vytvořen, automaticky volá destruktor CloseHandle uvolnit popisovač souboru.(V případě potřeby můžete použít (Active Template LIBARY) CHandle třídy pro tento účel stejné nebo unique_ptr spolu s vlastní deleter.) DiffHandles Funkce zjistí chyb čtení a potom vyvolá C++ výjimky.DiffFiles Funkce vyvolá ani zachytí všechny výjimky, a ještě má výjimku bezpečné.Umožňuje pouze všechny výjimky, chcete-li rozšířit až na vrchol zásobníku volání.Všechny funkce poskytnout záruku silného výjimky; Pokud v libovolném bodě těchto funkcí je vyvolána výjimka, jsou prozrazeny žádné zdroje a žádný stát program je upraven.
#include <Windows.h>
#include <iostream>
#include <string>
#include <stdexcept>
using namespace std;
class Win32Exception : public runtime_error
{
DWORD err;
static const int BUF_SIZE = 1024;
string msg;
string localMsg;
public:
Win32Exception(DWORD error, string msg): runtime_error(string("Win32Exception")), err(error), localMsg(msg) {}
// Generic message output function.
const char* what()
{
char buf[BUF_SIZE];
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, err, 0, (LPSTR) &buf, BUF_SIZE - 1, 0);
msg = string(buf) + ":" + localMsg;
return msg.c_str();
}
const DWORD GetErrorCode() {return err;}
};
void ThrowLastErrorIf(bool expression, string msg)
{
if (expression)
{
throw Win32Exception(GetLastError(), msg);
}
}
bool DiffHandles(HANDLE file1, HANDLE file2)
{
const int BUFFERLENGTH = 1024;
char buffer1[BUFFERLENGTH] = {'\0'};
char buffer2[BUFFERLENGTH] = {'\0'};
DWORD bytesRead = 0;
BOOL result = ReadFile(file1, buffer1, BUFFERLENGTH - 1, &bytesRead, NULL);
ThrowLastErrorIf(result == FALSE, string("File1"));
result = ReadFile(file2, buffer2, BUFFERLENGTH - 1,&bytesRead, NULL);
ThrowLastErrorIf(result == FALSE, string("File2"));
string s1(buffer1);
string s2(buffer2);
return s1 == s2;
}
class File
{
private:
HANDLE handle;
// Declared but not defined, to avoid double closing.
File& operator=(const File&);
File(File&);
public:
File(const wchar_t* file)
{
handle = CreateFile(file, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
ThrowLastErrorIf(handle == INVALID_HANDLE_VALUE, GetFileName(file));
}
HANDLE Get()
{
return handle;
}
string GetFileName(const wchar_t* f)
{
char buf[1024] = {'\0'};
wcstombs(buf, f, 1024 -1);
return string(buf);
}
~File()
{
CloseHandle(handle);
}
};
bool DiffFiles(const wchar_t* file1, const wchar_t* file2)
{
File f1(file1);
File f2(file2);
bool result = DiffHandles(f1.Get(), f2.Get());
return result;
}
int main()
{
try
{
bool result = DiffFiles(L"file1.txt",
L"file2.txt");
if (!result)
{
cout << "Files do not match." << "\n";
}
else
{
cout<< "Files match." << "\n";
}
}
catch(Win32Exception& e)
{
cout << e.what() << "\n";
}
cout << "Press any key" << "\n";
char c;
cin >> c;
}
Volání výjimečných kódu z jiných výjimečných kódu
Funkcí jazyka C++, které jsou deklarovány jako "extern C" mohou být volány programy C.Servery C++ COM může být zaplněno kód napsaný v některém z několika různých jazyků.Při implementaci veřejné funkce podporující výjimek v C++ na jiných výjimečných kódem nesmí povolit funkce C++ všechny výjimky, které šíří zpět k volajícímu.Funkce C++ proto musí zachytit konkrétně každé výjimce, která ví, jak zpracovat a v případě potřeby převést srozumitelného volající kód chyby výjimku.Pokud je známo, že ne všechny potenciální výjimky, měl by mít funkce C++ catch(…) blok jako poslední obslužné rutiny.V takovém případě je nejlepší zpráva závažná chyba volajícímu, protože váš program může být v neznámém stavu.
Následující příklad ukazuje funkci, která předpokládá, že všechny výjimky, které mohou být vyvolány Win32Exception nebo následovaném typem výjimky odvozené z std::exception.Funkce zachytí jakoukoli výjimku z těchto typů a šíří informace o chybě jako kód chyby Win32 k volajícímu.
BOOL DiffFiles2(const wchar_t* file1, const wchar_t* file2)
{
try
{
File f1(file1);
File f2(file2);
if (!DiffHandles(f1.Get(), f2.Get()))
{
SetLastError(MY_APPLICATION_ERROR_FILE_MISMATCH);
return FALSE;
}
return TRUE;
}
catch(Win32Exception& e)
{
SetLastError(e.GetErrorCode());
}
catch(std::exception& e)
{
SetLastError(MY_APPLICATION_GENERAL_ERROR);
}
return FALSE;
}
Při převodu z výjimek na chybové kódy jeden možný problém je, že kódy chyb často neobsahují velké množství informací, které lze ukládat výjimku.To lze vyřešit, můžete poskytnout catch blok pro každý typ výjimky, který může být vyvolána a provádět protokolování zaznamenat podrobnosti o výjimce, dříve, než je převeden na chybový kód.Tento přístup může vytvořit velké množství opakování kódu, je-li více funkcí, které všechny používají stejnou sadu catch bloky.Je vhodný způsob, jak se vyhnout opakování kódu refaktoring tyto bloky do jedné soukromé funkce implementující try a catch blokuje a přijímá objekt funkce, která je vyvolána v try bloku.V každé veřejné funkci předáte kód funkce nástroje jako lambda výraz.
template<typename Func>
bool Win32ExceptionBoundary(Func&& f)
{
try
{
return f();
}
catch(Win32Exception& e)
{
SetLastError(e.GetErrorCode());
}
catch(const std::exception& e)
{
SetLastError(MY_APPLICATION_GENERAL_ERROR);
}
return false;
}
Následující příklad ukazuje, jak lambda výraz, který definuje functor.Po functor definované "vložené" pomocí lambda výraz, je často čitelnější než by byly, pokud byly zapsány jako objekt s názvem funkce.
bool DiffFiles3(const wchar_t* file1, const wchar_t* file2)
{
return Win32ExceptionBoundary([&]() -> bool
{
File f1(file1);
File f2(file2);
if (!DiffHandles(f1.Get(), f2.Get()))
{
SetLastError(MY_APPLICATION_ERROR_FILE_MISMATCH);
return false;
}
return true;
});
}
Další informace o lambda výrazů naleznete v tématu Lambda výrazy v jazyce C++.
Viz také
Koncepty
Chyby a zpracování výjimek (moderní Příručka programování C++)