#c #stl #raii #code-structure #finalization
#c #stl #raii #структура кода #доработка
Вопрос:
В этом примере я столкнулся с проблемой копирования кода:
void BadExample1() {
if (!Initialize1())
return;
if (!Initialize2()) {
Finalize1();
return;
}
if (!Initialize3()) {
Finalize1();
Finalize2();
return;
}
if (!Initialize4()) {
Finalize1();
Finalize2();
Finalize3();
return;
}
// some code..
Finalize1();
Finalize2();
Finalize3();
Finalize4();
}
Bnd здесь представляет собой плохую структуру кода. Если у меня будет много конструкций, ширина кода будет слишком большой, это тоже плохо:
void BadExample2() {
if (Initialize1()) {
if (Initialize2()) {
if (Initialize3()) {
if (Initialize4()) {
if (Initialize5()) {
// some code..
Finalize5();
}
Finalize4();
}
Finalize3();
}
Finalize2();
}
Finalize1();
}
}
Как я могу сохранить хорошую структуру кода и решить проблему копирования кода?
Finalize1 /2 /3 — это функции API, а не мои программные классы.
Может быть, некоторые контейнеры STL могут решить эту проблему?
Может быть, что-то вроде этого?
void GoodExample() {
if (!Initialize1())
return;
RaiiWrapper<void(*)()> raii_wrapper1([]() {
Finalize1();
});
if (!Initialize2()) {
//Finalize1();
return;
}
RaiiWrapper<void(*)()> raii_wrapper2([]() {
Finalize2();
});
if (!Initialize3()) {
//Finalize1();
//Finalize2();
return;
}
RaiiWrapper<void(*)()> raii_wrapper3([]() {
Finalize3();
});
if (!Initialize4()) {
//Finalize1();
//Finalize2();
//Finalize3();
return;
}
RaiiWrapper<void(*)()> raii_wrapper4([]() {
Finalize4();
});
// some code..
//Finalize1();
//Finalize2();
//Finalize3();
//Finalize4();
}
Комментарии:
1. Да, именно так вы решаете эту проблему. Такие классы, как
RaiiWrapper
называются защитниками области . Их нет в стандартной библиотеке, но реализовать их тоже не сложно.2. @HolyBlackCat: Смотрите мой ответ.
Ответ №1:
Почему бы не использовать реальные объекты?
struct SetupPart1 {
SetupPart1 () { if (!Initialize1() throw std::runtime_error("Part1"); }
~SetupPart1 () { Finalize1(); }
};
и так далее для части 2, 3, 4 и т.д.
Теперь ваш пример выглядит так:
void GoodExample() {
try {
SetupPart1 p1;
SetupPart2 p2;
SetupPart3 p3;
SetupPart4 p4;
// some code ...
}
catch { const std::runtime_error amp;ex ) {
std::cerr << "GoodExample Failed: " << ex.what << std::end;
}
}
Ответ №2:
Вы могли бы упростить предложение Маршалла и использовать еще не стандартизированную std::make_unique_resource()
(эта функция тесно связана с scope_guard
, хитроумным решением, предложенным Андреем Александреску несколько лет назад, а также в этом предложении). Это дает вам объект с двумя функциями — одна для запуска в начале области видимости переменной, другая для запуска в ее конце (т. Е. при создании и уничтожении соответственно).
Тогда вместо определения четырех отдельных классов вы бы просто написали:
void GoodExample() {
auto r1 = std::make_unique_resource(Initialize1, Finalize1);
auto r2 = std::make_unique_resource(Initialize2, Finalize2);
auto r3 = std::make_unique_resource(Initialize3, Finalize3);
auto r4 = std::make_unique_resource(Initialize4, Finalize4);
// some code
}
В предложении есть код для реализации; и — это совсем не сложно. Таким образом, вы могли бы просто скопировать реализацию и создать свою собственную not_std::make_unique_resource()
функцию и связанные с ней шаблонные классы.
Комментарии:
1. Технически бесполезно, пока оно не стандартизировано, но все же стоит проголосовать.
2. @user4581301: Это полезно — можно было бы просто реализовать в предложении, которое даже не такое длинное. См. Редактирование.
Ответ №3:
Всякий раз, когда вы получаете ценный ресурс из API, вам нужно обернуть его как объект с соответствующим деструктором. Итак, если Initialize1
инициализируется something1
, то something1
действительно должен быть объект Something1
, который знает, как инициализировать и как завершить себя. Кроме того, сбой инициализации должен вызвать исключение (это не выполняется с помощью, fstream
потому что fstream
старше этой концепции).
class Something1 {
public: Something1 () { if (!Initialize1()) throw resource_failed ("1"); }
~Something1 () { Finalize1(); }
}