Пользовательские исключения C ++: производительность во время выполнения и передача исключений из C ++ в C (как информация об ошибке) - PullRequest
2 голосов
/ 21 апреля 2010

Я пишу пользовательский класс исключений C ++ (чтобы я мог передавать исключения, возникающие в C ++, на другой язык через C API).

Мой первоначальный план атаки заключался в следующем:

//C++ 
myClass
{
public:
   myClass();
   ~myClass();

   void foo() // throws myException
   int foo(const int i, const bool b) // throws myException
} * myClassPtr;


// C API
#ifdef __cplusplus
extern "C" {
#endif

myClassPtr MyClass_New();
void MyClass_Destroy(myClassPtr p);
void MyClass_Foo(myClassPtr p);
int  MyClass_FooBar(myClassPtr p, int i, bool b);

#ifdef __cplusplus
};
#endif

Мне нужен способ передать исключения, сгенерированные в коде C ++, на сторону C. Информация, которую я хочу передать стороне C, следующая:

(а). Какие (Б). куда (С). Простая трассировка стека (просто последовательность сообщений об ошибках в порядке их появления, отсутствие отладочной информации и т. Д.)

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

Таким образом, метод C ++, представленный в C API, будет реализован примерно так:

// Я должен признать, что есть что-то странно «хакерское» в реализации такого рода (за исключением кошмара обслуживания) - должен ли быть лучший способ?

// ExceptionInfo default ctor initialized to no error

void MyClass::foobarTrappedExceptionWrapper(ExceptionInfo& ei)
{    
    try {
      // call foobar ...
      foobar();
    }
    catch(fast_exception& fe)
    {
       switch (fe.errcode())
       {
          //package exception info into ei for C consumption 
       }
    }
    catch(std::exception &e)
    {    
      //package exception info into ei for C consumption 
    }
}

Реализация каждого из методов C ++, представленных в C API, должна быть заключена в оператор try / catch (см. Фрагмент выше). Влияние производительности на это кажется довольно серьезным (согласно этой статье ):

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

В то же время я помню, как читал где-то в мои дни на С ++, что, хотя обработка исключений стоит дорого, она становится дорогой только тогда, когда действительно происходит исключение. Итак, что правильно? что делать?. Есть ли альтернативный способ, которым я могу безопасно перехватывать ошибки и передавать полученную информацию об ошибках в C API ?. Или это незначительное соображение (в конце концов, статья довольно старая, и с тех пор аппаратные средства немного улучшились).

[Изменить]

Удален вопрос2, поскольку я нашел способ создать простую трассировку стека здесь .

1 Ответ

2 голосов
/ 21 апреля 2010

Ответы

  1. Оба верны; Вы неверно истолковываете первый источник. Блок try {} в основном не требует затрат; это бросок .... улов (то есть распространение), который дорог. Таким образом, это дешево, если исключение не происходит, поэтому их не следует использовать, если они происходят часто (потому что тогда вы бросаете ... часто ловите).
  2. Использование очень длинного буфера фиксированного размера не легче, чем использование std :: string. Использование std :: string является плохой идеей, только если вы беспокоитесь, что вы можете вызвать исключение из-за нехватки памяти (в этом случае конструктор std :: string выбросит, потому что он не может выделить место). Однако зачем изобретать велосипед? Я предлагаю вам взглянуть на boost :: exception , который предоставляет класс boost :: exception, который позволяет произвольным метаданным присоединяться к объекту исключения, и он может быть как легкий (без метаданных) или в зависимости от используемых вами метаданных.

Комментарий к передаче исключений C ++ в C
Выбор наследовать или не наследовать от std :: exception не влияет на производительность; в статье упоминался тот факт, что распространение исключения и выполнение раскрутки стека обходятся дорого (независимо от типа исключения). С точки зрения передачи исключения в мире C .... обработка ошибок в C обычно выполняется с использованием целочисленных типов возврата и кодов ошибок, хотя, если вы также хотите передать объект C ++, тогда, если вы можете полиморфно копировать тип исключения, тогда вы можете просто создать копию в куче, а затем передать объект исключения как тип void *, и если вам нужно извлечь информацию из этого объекта, то вы можете создать интерфейсы C которые реализованы в C ++ и которые приводят объект void * к указателю на ваш тип исключения и извлекают соответствующие поля. Альтернативой, если все, что вам нужно, является сообщение what (), это передача strdup () сообщения. Имейте в виду, однако, что все, что требует выделения / копирования, не будет работать, если std :: bad_alloc является одним из исключений вашей обработки (другими словами, если вы можете потерпеть неудачу из-за нехватки памяти, выделение большего количества памяти не очень хорошая идея).

Как говорится, «когда в Риме, делай, как делают римляне» .... мой собственный совет - использовать коды состояния возврата и коды ошибок в C и использовать обработку исключений в C ++. Я действительно не стал бы пытаться перенести объект исключения C ++ в мир C. Также имейте в виду, что для кода C ++ чаще вызывать код C, чем для кода C вызывать код C ++. Если вы создаете взаимодействия C для своего кода C ++, то они, вероятно, должны вести себя как интерфейсы C, а именно возвращать коды ошибок. Если ваш код C ++ вызывает функцию C, которая возвращает код состояния ошибки, то разумно генерировать исключение, а не возвращать код состояния ошибки, когда основной код C не удался. В этом случае, однако, вы находитесь в пространстве C ++ и вам не нужно беспокоиться о переходе исключения обратно в код C. Но в случае, если вы вернетесь обратно в мир C, я бы посоветовал записывать любую имеющуюся у вас информацию и использовать коды ошибок.

Кроме того, учитывая, что std :: exception :: what () возвращает объект char *, описывающий то, что произошло, в то время как ваш API возвращает целочисленный код, указывающий тип исключения ... ну, это действительно будет сбивать с толку других людей используя ваш код Когда я увидел, что вы используете waht () в операторе switch, я собирался прокомментировать, что вы не можете включить строку, а затем мне пришлось сделать двойной дубль и понять, что это означает нечто иное, чем обычно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...