Condividi tramite


Procedura: Interfaccia tra il codice eccezionale e Non Eccezionale

In questo articolo viene descritto come implementare la gestione delle eccezioni coerenti modulo c++ e inoltre come convertire le eccezioni ai codici di errore ai limiti di eccezione.

Talvolta il modulo c++ è un'interfaccia al codice che non utilizza le eccezioni (codice°non eccezionale).Tale interfaccia è nota come limite di eccezione.Ad esempio, è necessario chiamare la funzione Win32 CreateFile nel programma C++.CreateFile non genera eccezioni; anziché impostare i codici di errore che possono essere recuperati dalla funzione di GetLastError.Se il programma C++ è complesso, pertanto in probabilmente si preferisce che i criteri in base alle eccezioni coerenti di gestione degli errori.E probabilmente non si desidera ignorare le eccezioni solo perché è un'interfaccia con il codice non eccezionale e non si desidera combinare i criteri in base all'eccezione e non based eccezione di errore nel modulo di C++.

Funzioni Non eccezionali chiamante da C++

Quando si chiama una funzione non eccezionale da C++, lo scopo è di eseguire il wrapping della funzione c++ che rileva eventuali errori e quindi eventualmente genera un'eccezione.Quando si progetta tale funzione wrapper, è necessario decidere quale tipo di garanzie di eccezione da fornire: NO primary throw, forte, oppure base.In secondo luogo, progettare la funzione in modo da rilasciare tutte le risorse, ad esempio, handle di file, correttamente quando viene generata un'eccezione.In genere, ciò significa che utilizza puntatori intelligenti o simili gestori di risorse al proprio le risorse.Per ulteriori informazioni sulle considerazioni di progettazione, vedere Procedura: Progettazione di sicurezza di eccezione.

Hh279691.collapse_all(it-it,VS.110).gifEsempio

L'esempio seguente mostra le funzioni C++ che utilizzano il CreateFile Win32 e ReadFile funziona internamente per aprire e leggere due file.La classe di File rappresenta un'acquisizione risorse è wrapper di (RAII) di inizializzazione per un handle di file.Il costruttore viene rilevato uno stato "impossibile trovare il file" e genera un'eccezione per propagare l'errore nello stack di chiamate del modulo C++.Se un'eccezione viene generata quando un oggetto di File completamente viene costruito, il distruttore chiama automaticamente CloseHandle per rilasciare l'handle di file.Se si preferisce, è possibile utilizzare la classe di (ATL) CHandle ATL (Active Template Library) per questo stesso scopo, o unique_ptr insieme a un deleter personalizzato.) La funzione di DiffHandles rileva gli errori di lettura quindi genera eccezioni C++.La funzione di DiffFiles né genera né intercetti tutte le eccezioni, ma è indipendente dalle eccezioni.Consente semplicemente che tutte le eccezioni si propaghino lo stack di chiamate.Tutte le funzioni forniscono una stretta garanzia di eccezione, se viene generata un'eccezione in un punto qualsiasi di queste funzioni, nessuna risorsa è e il cast alcuno stato del programma viene modificato.

#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;
}

Eccezionale codice chiamante dal codice Non eccezionale

Le funzioni C++ che vengono dichiarate come "extern" C possono essere chiamate dai programmi C.I server COM C++ possono essere utilizzati da codice creato in uno di numerosi linguaggi diversi.Quando si distribuiscono le eccezioni funzioni dipendenti dal pubblico in C++ da chiamare dal codice non eccezionale, la funzione C++ non deve consentire alcune eccezioni per la propagazione del chiamante.Pertanto, la funzione C++ è specificamente rilevare tutte le eccezioni che è in grado di gestire e, se richiesto, convertire l'eccezione a un codice di errore che il chiamante accettato.Se non tutte le eccezioni possibili sono note, la funzione C++ devono avere un blocco di catch(…) come ultimo gestore.In questo caso, è consigliabile segnalare un errore irreversibile al chiamante, perché il programma potrebbe essere in uno stato sconosciuto.

Nell'esempio seguente viene illustrata una funzione che presuppone che qualsiasi eccezione può essere generata è un Win32Exception o un tipo di eccezione derivato da std::exception.La funzione intercetti tutte le eccezioni di questi tipi e propaga informazioni su come codice di errore Win32 al chiamante.

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; 
} 

Quando si converte le eccezioni nei codici di errore, un problema potenziale è che i codici di errore non contengono spesso la ricchezza di informazioni che un'eccezione può archiviare.Per risolvere questo problema, è possibile fornire un blocco di catch per ogni tipo di eccezione specifico che venga generato e si esegue la registrazione per registrare i dettagli dell'eccezione prima di essere convertita in un codice di errore.Questo approccio può creare più ripetizione di codice se più eseguito qualsiasi utilizzo dello stesso set di catch blocco.Una tecnica efficace per evitare la ripetizione di codice è il refactoring tali blocchi in una funzione pratica privata che implementa i blocchi di catch e di try e accetta un oggetto funzione richiamato nel blocco di try.In ogni funzione pubblica, passare il codice alla funzione pratica come un'espressione lambda.

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; 
} 

Di seguito viene illustrato come scrivere l'espressione lambda che definisce il functor.Quando un functor è "inline" definito utilizzando un'espressione lambda, è spesso più facile da leggere che verrebbe se fosse scritto come oggetto funzione denominato.

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

Per ulteriori informazioni sulle espressioni lambda, vedere Espressioni lambda in C++.

Vedere anche

Concetti

Errori e gestione delle eccezioni C++ (moderno)

Procedura: Progettazione di sicurezza di eccezione