2010-09-20 5 views
3

Когда у меня есть метод, который вызывает набор методов, которые обеспечивают надежную гарантию, у меня часто возникает проблема с откатом изменений, чтобы также иметь сильный метод гарантии. Давайте используем пример:Сильный метод гарантии, вызывающий сильные методы гарантии

// Would like this to offer strong guarantee 
void MacroMethod() throw(...) 
{ 
    int i = 0; 
    try 
    { 
    for(i = 0; i < 100; ++i) 
     SetMethod(i); // this might throw 
    } 
    catch(const std::exception& _e) 
    { 
    // Undo changes that were done 
    for(int j = i; j >= 0; --j) 
     UnsetMethod(j); // this might throw 
    throw; 
    } 
} 

// Offers strong guarantee 
void SetMethod(int i) throw(...) 
{ 
    // Does a change on member i 
} 

// Offers strong guarantee 
void UnsetMethod() throw(...) 
{ 
    // Undoes a change on member i 
} 

Очевидно, что UnsetMethod может выбрасывать. В этом случае мой MacroMathod() предлагает только базовую гарантию. Тем не менее, я сделал все, что мог, чтобы предложить надежную гарантию, но я не могу быть абсолютно уверен, что мой UnsetMethod() не будет бросать. Вот мои вопросы:

  1. Должен ли я попытаться предложить сильную гарантию в этом случае?
  2. Должен ли я документировать свой MacroMethod() как имеющий базовую или сильную гарантию? Даже если это очень маловероятно, UnsetMethod будет бросать?
  3. Можете ли вы увидеть способ сделать этот метод действительно надежной гарантией?
  4. Возможно, мне следовало бы поставить вызов UnsetMethod() в попытке, но это кажется довольно тяжелым, и что мне делать в catch?

Спасибо!

+0

Я видел только сильные гарантии были применены к не методам стоящих функций. Я полагаю, это можно сделать, но нам действительно нужно понять контекст больше. Меняя член i на то, что именно есть какая-то глобальная переменная, мы меняем состояние? Если это объект. Затем вы делаете копию. Предварительно создавайте операции, если все они работают, тогда вы заменяете реальную. –

+0

Как примечание стиля (с некоторыми довольно неприятными последствиями), вы должны пересмотреть использование спецификаций исключений. Известно, что они вызывают серьезные проблемы, пытаясь полностью удалить их с языка. –

+0

@Stanley: Было обсуждение с другом относительно этого. Проделали также некоторые тесты с компилятором и прочитали рациональное повышение. Ты прав. Я, хотя эта критика/ограничение заключалась в том, чтобы указывать типы в спецификации, а не в спецификации вообще. Это объясняет, почему я удерживал его, чтобы бросить() против броска (...). Спасибо за комментарий и обновим мой код. – Geeho

ответ

6

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

Этот метод имеет то преимущество, что вам не нужны никакие try...catch -блоки в вашем коде, а также код очистки. Если выбрано исключение, измененная копия отбрасывается во время разворачивания стека, и оригинал вообще не был изменен.

+0

В моем примере не была описана полная степень проблемы, не говоря уже о проблемах производительности, которые это решение может вызвать: член является массивом данных, и такие операции могут выполняться регулярно. Но предлагаемое вами решение приносит мне правильное решение, которое, по сути, вы предлагаете. Я добавлю методы SetCachedMethod() и ApplyCache()/ClearCache(). SetCachedMethod() будет бросать, применять и очищать не будет. Поэтому у меня есть частичная копия объекта, а не полная. Благодаря! – Geeho

+0

@ Geeho: Возможно, вы можете сделать кеш внешним объектом или связать вызов clear-cache с каким-то защитником (посмотрите [RAII] (http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization)). Таким образом, вы по-прежнему можете предотвратить попытку блокировки catch в вашем коде. –

+0

Не совсем плохая идея. Посмотрите на это первое утро завтра утром. – Geeho

1

Если важная важная гарантия важна для MacroMethod(), я бы пересмотрел UnsetMethod(), чтобы ничего не бросить, если можно. Конечно, как это можно сделать, зависит от того, что вы делаете.

Вы используете UnsetMethod() для очистки после отказа от SetMethod(). Если UnsetMethod() не удается очистить, что вы можете сделать по этому поводу? Это та же самая причина, почему бросание исключений из деструкторов чрезвычайно опасно.

1
  1. Нет, вы не можете вообще с кодом, который вы указали. Однако, в зависимости от вашей проблемы, возможно, вы можете сделать копию данных, выполнить SetMethod на этой копии и затем поменять представление. Это обеспечивает надежную гарантию, но опять же зависит от проблемы.
  2. Вы можете документировать: Сильная гарантия, если UnsetMethod не выбрасывает, в противном случае. На самом деле это объясняет, почему сказано, что деструкторы не должны бросать. Фактически любые операции отмены не должны бросаться.
  3. Да, см. 1.
  4. Нет, это не имеет никакого смысла.
0

Фундаментально, что вам нужно сделать в функции, которые требуют метательных функций работа на временную копию данных, пока все, возможно, метательной подфункции закончила, то swap (который не должен бросить) временный значения в «реальную» структуру.

Некоторого иллюстративный psudocode:

void MacroMethod() throw(...) 
{ 
    int i = 0; 
    MyValues working_copy[100] = *this; //obviously this is psudocode as I don't know your real code 
    for(i = 0; i < 100; ++i) 
    working_vopy[i].SetMethod(i); // if this throws the exception will propogate out, and no changes will be made 
    swap(*this, working_copy); // this must not throw 
} 
Смежные вопросы