Альтернатива исключению на С++

Я пишу реактивное программное обеспечение, которое неоднократно получает входные данные, обрабатывает его и испускает соответствующий вывод. Основной цикл выглядит примерно так:

initialize();
while (true) {
    Message msg,out;
    recieve(msg);
    process(msg,out);
    //no global state is saved between loop iterations!
    send(out);
}

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

Исключение С++ исключительно полезно для этой ситуации, я мог бы окружать process предложением try/catch и бросать исключение всякий раз, когда что-то идет на wrog. Единственное, что мне нужно, чтобы я очистил все свои ресурсы, прежде чем выбросить исключение. Это может быть проверено RAII или написанием глобального распределителя ресурсов (например, если ваш деструктор может вызвать исключение) и использовать его исключительно для всех ресурсов.

Socket s = GlobalResourceHandler.manageSocket(new Socket());
...
try {
    process(msg,out);
catch (...) {
    GlobalResourceHandler.cleanUp();
}

Однако использование исключений запрещено в нашем стандарте кодирования (также в Google С++ standard BTW), в результате весь код скомпилирован с помощью исключений, и я считаю, что никто не собирается менять способ работы всего лишь для моей проблемы с дизайном.

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

Есть ли альтернативный дизайн, который я могу рассмотреть?

обновление: Я ценю всякий ответ о стандарте идиотского кода. Единственное, что я могу сказать, - в крупных организациях вы должны иметь строгие и иногда нелогичные правила, чтобы убедиться, что идиот не придет и сделает ваш хороший код незабываемым. Стандарт больше о людях, чем о технических особенностях. Да, плохой человек может сделать каждый код беспорядочным, но это намного хуже, если вы дадите ему дополнительные инструменты для этой задачи.

Я все еще ищу технический ответ.

Ответы

Ответ 1

Кодирование этих видов услуг в течение всего дня Я понимаю вашу проблему. Хотя у нас есть исключения в нашем коде, мы не возвращаем их во внешние библиотеки, которые его вызывают, вместо этого у нас есть простой "tribool".

enum ReturnCode
{
  OK = 0,  // OK (there is a reason for it to be 0)
  KO,      // An error occurred, wait for next message
  FATAL    // A critical error occurred, reboot
};

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

С++ здесь приносит много всего с RAII, поскольку он смеется с несколькими путями возврата и гарантирует детерминированное освобождение объектов, которые он держит.

Для фактической проверки кода вы можете просто использовать некоторые макросы:

// Here is the reason for OK being 0 and KO and Fatal being something else

#define CHECK_RETURN(Expr) if (ReturnCode code = (Expr)) return code;

#define CHECK_BREAK(Expr) if (ReturnCode code = (Expr)) \
    if (KO == code) break; else return code;

Тогда вы можете использовать их так:

CHECK_RETURN( initialize() )
while(true)
{
  Message msg,out;
  CHECK_BREAK( receive(msg) )
  CHECK_BREAK( process(msg,out) )
  CHECK_BREAK( send(out) )
}

Как уже отмечалось, реальный облом - это конструкторы. Вы не можете иметь "нормальных" конструкторов с такой ситуацией.

Возможно, вы можете использовать boost::optional, если не можете, я бы действительно предложил дублировать функциональность. Объедините это с системными factory функциями вместо конструкторов, и вы уходите:

boost::optional<MyObject> obj = MyObject::Build(1, 2, 3);
if (!obj) return KO;

obj->foo();

Похоже на указатель, за исключением того, что он выделяется стек и, следовательно, имеет почти нулевые служебные данные.

Ответ 2

Если вы не можете throw исключение, альтернативой является return (или return false или аналогичный код ошибки).

Если вы бросаете или возвращаете, вы все равно используете детерминированные деструкторы С++ для освобождения ресурсов.

Единственное, с чем вы не можете просто просто "вернуться", это конструктор. Если у вас есть неисправимая ошибка в конструкторе, то это хорошее время для броска; но если вам не разрешено бросать, то вместо этого вы должны вернуться, и в этом случае вам понадобится другой способ сообщить о сбое конструкции:

  • Имейте частные конструкторы и статические методы factory; если метод factory возвращает значение null при ошибке построения; не забудьте проверить нулевой возврат, когда вы вызываете метод factory
  • У вас есть свойство get_isConstructedOk(), которое вы вызываете после каждого конструктора (и не забывайте вызывать/проверять его на каждом вновь построенном объекте).
  • Реализуйте "двухэтапную" конструкцию: в которой вы говорите, что любой код, который может выйти из строя, не должен быть в конструкторе, а должен быть в отдельном методе bool initialize(), который вызывается после конструктора (и не должен забудьте вызвать initialize и не забудьте проверить его возвращаемое значение).

Ответ 3

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

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

Ответ 4

Однако использование исключения исключается в нашем стандарте кодирования (также в стандарте Google С++ BTW). Есть ли альтернативный дизайн, который я могу рассмотреть?

Стандарты кодирования - это гайки.

Я предлагаю вам спросить человека/людей, которые разработали и утвердили этот стандарт, как решить вашу проблему. Если у них нет хорошего ответа, используйте это как оправдание для игнорирования этой части стандарта кодирования... конечно, с разрешения вашего босса.

Ответ 5

Однако использование исключения запрещено в нашем стандарте кодирования (также в Стандарт Google С++ BTW). Здесь альтернативный дизайн, который я могу рассмотреть?

Короткий ответ нет.

Длинный ответ да:). Вы можете заставить все функции вернуть код ошибки (аналогично реализации платформы Microsoft COM.

Основными недостатками этого подхода являются:

  • вы должны явно обрабатывать все исключительные случаи

  • размер вашего кода резко возрастает

  • код становится более трудным для чтения.

Вместо:

initialize();
while (true) {
    Message msg,out;
    recieve(msg);
    process(msg,out);
    //no global state is saved between loop iterations!
    send(out);
}

у вас есть:

if( !succeedded( initialize() ) )
    return SOME_ERROR;

while (true) {
    Message msg,out;
    if( !succeeded( RetVal rv = recieve(msg) ) )
    {
         SomeErrorHandler(rv);
         break;
    }
    if( !succeeded( RetVal rv = process(msg,out) ) )
    {
         SomeErrorHandler(rv);
         break;
    }
    //no global state is saved between loop iterations!
    if( !succeeded( RetVal rv = send(out) ) )
    {
         SomeErrorHandler(rv);
         break;
    }
}

Кроме того, реализация всех ваших функций должна будет сделать то же самое: объединить каждый вызов функции с помощью if.

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

Короче говоря, за исключением, возможно, использования RAII в вашем коде и шаблонах (разрешено ли вам их использовать?), вы оказываетесь ближе к "C-коду, используя компилятор С++".

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

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

Это хороший случай представить тому, кто отвечает за стандарты кодирования в вашей компании: (

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

Ответ 6

Если вы работаете под окнами, вы можете использовать исключения SEH. Они также имеют преимущество обработчика pre-stack-unwind, который может остановить разматывание (EXCEPTION_CONTINUE_EXECUTION).

Ответ 7

Сверху моей головы вы можете добиться чего-то подобного с сигналами.

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

Там может быть больше, чем это, поскольку я не думал об этом.

Надеюсь, что это поможет.

Ответ 8

Вы вызываете какие-либо библиотеки, которые могут создавать исключения? Если это так, вам понадобится попытка поймать в любом случае. Для внутренних ошибок каждому методу необходимо вернуть код ошибки (используйте return только для кода ошибки, используйте ссылочные параметры для возврата фактических значений). Если вы хотите сделать очистку памяти на 100% надежной, вы можете запустить приложение с помощью приложения монитора. Если ваше приложение выйдет из строя, монитор снова запустит его. Вам все равно нужно закрыть файлы и соединение с БД, tho.

Ответ 9

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

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

Например

#define WHILE_R(cond,return_value) while (cond) {\
    if (exception_thrown) return return_value
#define ENDWHILE() }

bool isPolyLegal(Poly p) {
    PolyIter it(p);
    WHILE_R(it.next(),true) //return value is arbitary
    ...
        if (not_enough_memory) exception_thrown = true;
    ...
    ENDWHILE()
}