Поделиться через


Практическое руководство. Интерфейс между исключительным и неисключительным кодом

В этом разделе описываются способы реализации согласованной обработки исключений в модуле A.C, C-++, а также, как перевести те исключения и наоборот кодов ошибок в диапазоне исключения.

Иногда модуль A.C на C-++ интерфейс с кодом, не использует исключения (неисключительный код).Такой интерфейс известная в виде исключения границы.Например, может потребоваться вызов функции CreateFile Win32 в программе C C-++.CreateFile не создает исключения; вместо этого он задает коды ошибок, которые могут быть получены функцией GetLastError.Если в программе C C-++ нетривиальна, в которой возможно, необходимости иметь последовательной обработки ошибок исключений на основе политики., Не смогут отказаться исключения только потому, что интерфейс неисключительным с кодом, и ни одного требуется смешивания исключений и ошибок не-исключение- политики на основе в модуле C C-++.

Вызов не исключительные функции C из C-++

При вызове функции не исключительная в C, C-++, рекомендуется создать эту функцию в функции A.C, C-++, обнаруживает каких-либо ошибок и затем, возможно, возникает исключение.При разработке такую функцию оболочки, сначала определите тип исключения, которое будет представлен: не запущенные, строгую или базовый.Во-вторых, создайте функцию так, чтобы все ресурсы, например дескрипторы файлов, правильно будут освобождены при возникновении исключения.Как правило, это означает, что используется интеллектуального указателя или похожие диспетчеры ресурсов к собственному ресурсы.Дополнительные сведения о конструктивных соображениях см. в разделе Практическое руководство. Конструктор для исключения Безопасности.

Hh279691.collapse_all(ru-ru,VS.110).gifПример

В следующем примере показаны функции C, C-++, которые используют Win32 CreateFile и ReadFile внутренне для открытия и считывания 2 файла.Класс File получение ресурса программа-оболочка (RAII) для инициализации дескрипторов файлов.Его конструктора определяет состояние файла "," и создает исключение для распространения ошибку вверх по стеку вызовов модуля C C-++.Если исключение создается, после создания объекта File полностью построен, деструктор автоматически вызывает метод CloseHandle, чтобы освобождать дескриптор файла.(При необходимости можно использовать класс CHandle (ATL) библиотеку шаблонных классов ATL для этой одной цели, или unique_ptr вместе с пользовательским deleter). Функция DiffHandles обнаруживает ошибки чтения и затем вызывает исключения C C-++.Функция DiffFiles не создает и не перехватывает все исключения, а также исключений безопасным образом.Просто позволяет все исключения для распространения вверх по стеку вызовов.Все функции предоставляют сильную гарантию исключения; если исключение создается в любой момент в эти функции, то остальные ресурсы не протекаены и нет состояния программы не изменяется.

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

Вызов кода из кода неисключительного исключительный

Функция C, C-++, объявленные как extern "C" могут вызываться для программ на языке C - программами.Серверы модели COM C C-++ могут использоваться в коде, созданном на любом из нескольких различных языков.При реализации функции общего исключения языковых в C, C-++, который будет вызывать неисключительный код функции C, C-++ не может разрешить все исключения для распространения обратно вызывающему объекту.Поэтому функция C, C-++ должна определить каждое исключение, он знает, как обрабатывать и, при необходимости, преобразует исключение в код ошибки, который понимает вызывающий объект.Если не все возможные исключения известно, что функция C C-++ должна содержать блок catch(…) как последний обработчик.В таком случае лучше уведомить неустранимую ошибку в вызывающий объект, так как программа может находиться в неизестном состоянии.

В следующем примере показаны функции, высказывать любые исключения, исключение может создаваться или Win32Exception или тип исключения, производный от std::exception.Функция перехватывающий любые исключения этих типов и распространяет сведения об ошибке как код ошибки Win32 вызывающему объекту.

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

При преобразовании из исключений в ошибки кодам, одна потенциальная проблема, коды ошибок не всего богатства часто содержат сведения, исключение может хранить.Чтобы исправить это, можно предоставить блок catch для каждого конкретного типа исключения, которое может быть возникает и запустите средство ведения журнала, чтобы записать данные исключения до его преобразования в код ошибки.Этот подход может создать много повторение кода, если несколько функций, используется один и тот же набор блоков catch.Удобный способ избежать повторения кода, выполнять эти блоки в одну включить функцию удобство использования, реализующий блоков try и catch и принимает объект функции, который вызывается в блоке try.В каждой открытой функции, передайте код для удобства использования функции как лямбда-выражение.

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

В следующем примере показано, как создать лямбда-выражение, которое определяет функтором.Если указано функтором "и" с помощью лямбда-выражения, часто проще читать, чем его, если он был записан как объект именем функции.

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

Дополнительные сведения о лямбда-выражениях см. в разделе Lambda expressions in C++.

См. также

Основные понятия

Ошибки и обработка исключений (самомоднейшее C++)

Практическое руководство. Конструктор для исключения Безопасности