Необходим ли провал / успех / выход из области D? - PullRequest
18 голосов
/ 08 августа 2009

При использовании языка, в котором есть try / catch / finally, еще ли полезны операторы D в области видимости сбоя / успеха / выхода? Кажется, что в D, наконец, нет, что может объяснить, почему эти операторы используются в D. Но с таким языком, как C #, это полезно? Я создаю язык, поэтому, если увижу много плюсов, я добавлю его.

Ответы [ 6 ]

39 голосов
/ 08 августа 2009

scope(X) необязательно так же, как for не обязательно, если у вас есть if и goto.

Вот перефразированный пример из кода, который я писал сегодня:

sqlite3* db;
sqlite3_open("some.db", &db);
scope(exit) sqlite3_close(db);

sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, "SELECT * FROM foo;", &stmt);
scope(exit) sqlite3_finalize(stmt);

// Lots of stuff...

scope(failure) rollback_to(current_state);
make_changes_with(stmt);

// More stuff...

return;

Сравните это с использованием try / catch:

sqlite3* db;
sqlite3_open("some.db", &db);
try
{
    sqlite3_stmt* stmt;
    sqlite3_prepare_v2(db, "SELECT * FROM foo;", &stmt);
    try
    {
        // Lots of stuff...
        try
        {
            make_changes_with(stmt);

            // More stuff...
        }
        catch( Exception e )
        {
            rollback_to(current_state);
            throw;
        }
    }
    finally
    {
        sqlite3_finalize(stmt);
    }
}
finally
{
    sqlite3_close(db);
}

Код превратился в спагетти , распространяющий исправление ошибок по всему магазину и заставляющий уровень отступа для каждого блока попытки. Версия, использующая область видимости (X), на мой взгляд, значительно более удобочитаема и понятна.

9 голосов
/ 08 августа 2009

try / catch / finally вызывает уровень вложенности; Охрана границ не делает. Кроме того, они позволяют писать код очистки в той же «области», что и код выделения, поэтому больше не нужно «открывать файл, прокручивать до конца функции, закрывать файл, прокручивать до верхней части функции».

По сути, это просто более удобное выражение обработки исключений try / catch / finally - что угодно , которое вы можете сделать с помощью try / catch / finally, вы можете сделать с защитой области видимости и наоборот

Стоит ли это того? Я фанат D (так, предвзято), но я бы сказал определенно.

6 голосов
/ 08 августа 2009

Отказ от ответственности Я тоже фанат D.

someRiskyFunctionThatMayThrow();
lock();
/* we have definitly got the lock so lets active
a piece of code for exit */
scope(exit)
    freelock();

По сравнению с:

try
{
    someRiskyFunctionThatMayThrow();
    lock();
}
finally
{
    freeLockIfNotGot();
}
5 голосов
/ 21 октября 2012

Стоит отметить, что область действия (выход), область действия (ошибка) и область действия (успех) также доступны для C ++.

  • Для области видимости (выход) есть библиотека Boost.ScopeExit .
  • Для области действия (сбой) и области действия (успех) существует библиотека stack_unwinding .

Поддерживается следующий синтаксис, случай 1:

try
{
    int some_var=1;
    cout << "Case #1: stack unwinding" << endl;
    scope(exit)
    {
        cout << "exit " << some_var << endl;
        ++some_var;
    };
    scope(failure)
    {
        cout << "failure " << some_var  << endl;
        ++some_var;
    };
    scope(success)
    {
        cout << "success " << some_var  << endl;
        ++some_var;
    };
    throw 1;
} catch(int){}

печать:

Case #1: stack unwinding
failure 1
exit 2

Дело 2:

{
    int some_var=1;
    cout << "Case #2: normal exit" << endl;
    scope(exit)
    {
        cout << "exit " << some_var << endl;
        ++some_var;
    };
    scope(failure)
    {
        cout << "failure " << some_var << endl;
        ++some_var;
    };
    scope(success)
    {
        cout << "success " << some_var << endl;
        ++some_var;
    };
}

печать:

Case #2: normal exit
success 1
exit 2
5 голосов
/ 08 августа 2009

Отличить сбой-выход от успешного-выхода иногда довольно полезно - у меня нет реального опыта работы с D, но утверждение Python with также позволяет это, и я считаю, что очень полезно, например, зафиксировать или откатить транзакцию БД, открытую в защищенной части тела.

Когда я объяснил эту тогдашнюю новую функцию Python (она уже давно существует ;-) друзьям и коллегам, которые являются гуру в C ++ и Java, я обнаружил, что они сразу поняли, и увидел интерес к такой функции ( В Python тоже есть finally, но это не помогает отличить успех от отказа, как и в других языках [или C ++ "RAII-уничтожение автоматических переменных в блоке" эквивалентно]).

2 голосов
/ 21 июня 2010

@ DK, Следует отметить, что в C ++ (и Java, я думаю) вы могли бы легко использовать "анонимный" класс для достижения того же, что и видимость (выход):

int some_func() 
{
    class _dbguard { sqlite3* db;
                     _dbguard(const _dbguard&); _dbguard& operator=(const _dbguard&);
                 public:
                     _dbguard(const char* dbname) { sqlite3_open(dbname, &db);}
                     ~_dbguard() {sqlite3_close(db);} 
                     operator sqlite3*() { return db; } 

    } db("dbname");
    ...
}

И если бы вы делали это не раз, вы бы сразу превратили его в полный класс, чтобы обработать ваш RAII для вас. Это так просто написать, я не могу представить программу на C ++, которая использует sqlite (как используется в примере) без создания классов, таких как CSqlite_DB и CSqlite_Stmt. На самом деле оператор sqlite3 * () должен быть анафамным, а в полной версии будут просто методы, предоставляющие операторы:

class CSqlite3_DB {
    ...
    CSqlite3_Stmt Prepare(const std::string& sql) {
        sqlite3_stmt* stmt = 0;
        try {
             sqlite3_prepare_v2(db, sql.c_str(), &stmt);
        } catch (...) {}
        return stmt;
    }
};

Что касается исходного вопроса, я бы сказал, что ответ «не совсем». Надлежащее уважение к DRY скажет вам взять эти длинные блоки try / catch / finally и преобразовать их в отдельные классы, которые скрывают части try / catch от остальных, где они могут (в случае области видимости (сбой)), и делают прозрачное управление ресурсами (в случае области (выход)).

...