Реализация "наконец" в C++0х


Я сделал быстрый и наконец тип в C++:

template<class F>
class finally_type {
public:

    explicit finally_type(F f) : function(f) {}
    ~finally_type() { try { function(); } catch (...) {} }

private:

    F function;

};


template<class F>
finally_type<F> finally(F f) { return finally_type<F>(f); }

(Отредактированы, чтобы добавить наркокурьером, чтобы не допустить исключения из распространения из обратного вызова.)

С C++0х, это удобно использовать этот класс следующим образом:

void test() {

    int* i = new int;

    auto cleanup = finally([i]() { delete i; });

    may_throw();

}

Независимо от того, may_throw() бросает, наконец, гарантирует, что код очистки выполняется, когда стек. Эквивалент идиома в C++03 использует локальный тип:

void test() {

    int* i = new int;

    struct finally {
        finally(int* i) : i(i) {}
        ~finally() { delete i; }
        int* i;
    } cleanup(i);

    may_throw();

}

Как вы можете видеть, закрытие должно быть явно удался, с большим количеством повторяющихся (и соответственно ошибок) код; лямбда, этот процесс существенно упрощается.

Честно говоря, я не вижу большого смысла, поскольку, если вы взаимодействовать с унаследованным кодом, вы, вероятно, просто собираюсь обернуть типов или использовать shared_ptr с пользовательским deleter. Вы могли использовать ее для управления низкого уровня блокировок в многопоточном коде, если это ваши фантазии по каким-то причинам.

В любом случае, предполагая, что кому-то это полезно, есть что-то важное, что я пропустил?



1198
5
задан 8 июня 2011 в 01:06 Источник Поделиться
Комментарии
1 ответ

Имейте в виду, что код внутри функции вызывается , наконец, должны не разрешать исключения, чтобы выйти наружу, или вы в конечном итоге с проблемами, когда деструктор вызывается при очистке исключение в вызывающий код. Возможно, вы захотите поставить предложение catch(...) блок в finally_type деструктор по этой причине.

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

Редактировать: если вы должны поддержать, не относящихся к RAII класса с произвольным метод очистки, то вы можете сделать областью держатель, который будет вызывать метод для вас. Это оказалось сложнее, чем я ожидал, вот лучшее, что я мог придумать:

class ScopedAction {
public:
virtual ~ScopedAction() = 0;
};

ScopedAction::~ScopedAction() {
}

template<class T, class Arg1, void (T::* p)(Arg1)>
class SpecialisedScopedAction : public ScopedAction {
public:
SpecialisedScopedAction(T &target, Arg1 &arg) : target_(target), arg_(arg) { }
~SpecialisedScopedAction() {
try {
(target_.*p)(arg_);
}
catch (...)
{}
}

private:
T &target_;
Arg1 &arg_;
};

class ScopedActionHolder {
public:
ScopedActionHolder(ScopedAction * action) : action_(action) {}
~ScopedActionHolder() {
delete action_;
}

private:
ScopedAction * action_;
};

template<class T, class Arg1, void (T::* fn)(Arg1)>
ScopedAction * makeScopedAction(T & t, Arg1 & arg) {
return new SpecialisedScopedAction<T, Arg1, fn>(t, arg);
}

Используя следующие относящихся к RAII класса:

enum TransactionState {
COMMIT,
ROLLBACK
};

class DBConnection {
public:
void finish(TransactionState state) {
if (state == COMMIT) {
cout << "committing transaction" << endl;
} else {
cout << "rolling back transaction" << endl;
}
}
};

Вы можете сделать объект области, что вызывает готово, способ, связывания параметров из области локальной так:

void test() {
DBConnection conn;
TransactionState tstate = ROLLBACK;

ScopedActionHolder cleanup(makeScopedAction<DBConnection, TransactionState, &DBConnection::finish>(conn, tstate));

cout << "Doing something" << endl;
tstate = COMMIT;
}

Указания типов аргументов шаблона, даже если они неявные функции типа указатель раздражает, я надеялся обойти это, но я бежал из времени. Кроме того, синтаксис является неочевидным и нужна новая перегрузка makeScopedAction функции для каждого разное количество параметров метода, которая нуждается в поддержке.

Подумай об этом, СТД::функциональная или использованием Boost::лямбда может быть как-нибудь помочь.

3
ответ дан 8 июня 2011 в 02:06 Источник Поделиться