Выбрасывание исключения в С++ в обратном вызове C, возможно пересечение границы динамической библиотеки... безопасно?

Я использую libjpeg, чтобы сохранить изображения JPEG. Если есть ошибка, поведение по умолчанию libjpeg - вызвать exit(), чего я хочу избежать, поскольку это не фатальная ошибка для моей программы. libjpeg позволяет вам использовать собственный диспетчер ошибок и требует, чтобы при использовании вашей собственной функции error_exit() (которая вызывает exit() по умолчанию ) вы должны не возвращать управление вызывающему. libjpeg предлагает использовать setjmp.h для удовлетворения этого требования, а не exit() программы.

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

Вот пример:

#include <iostream>
#include <jpeglib.h>
#include <cstdio>
#include <stdexcept>

static void handleLibJpegFatalError(j_common_ptr cinfo)
{
  (*cinfo->err->output_message)(cinfo);
  throw std::runtime_error("error in libjpeg, check stderr");
}

int main()
{
  struct jpeg_compress_struct cinfo;
  struct jpeg_error_mgr jerr;
  FILE* file = std::fopen("out.jpeg", "wb"); // assume this doesn't fail for this example

  try
    {
      cinfo.err = jpeg_std_error(&jerr);
      jerr.error_exit = handleLibJpegFatalError;

      // let say this triggers a fatal error in libjpeg and handleLibJpegFatalError() is called
      // by libjpeg
      jpeg_create_compress(&cinfo);
    }
  catch (...)
    {
      std::cerr << "Error saving the JPEG!\n";
    }

  jpeg_destroy_compress(&cinfo);
  std::fclose(file);
}

Я хотел бы знать: могу ли я сделать исключение из этого обратного вызова и уловить его в своем приложении, даже если libjpeg скомпилирован как динамическая библиотека? libjpeg может быть статическим или динамической библиотеки, и если это динамическая библиотека, она может быть построена с другим компилятором. Однако код, который выбрасывает и ловит исключение, безусловно, будет в одном модуле компиляции. Безопасен ли этот код?

FYI, я разрабатываю для OS X и Windows (и сохраняю в виду будущее возможности Linux), поэтому меня больше интересует, известно ли, что это хорошо определенное поведение в целом, а не для конкретной платформы/компилятора.

Ответы

Ответ 1

Это не безопасно. В зависимости от того, как был скомпилирован соответствующий код библиотеки, отличной от С++, необходимые таблицы разматывания могут отсутствовать. Это просто практическая причина, почему она может потерпеть неудачу; концептуальная причина в том, что это просто поведение undefined.

Вы должны следовать документации и использовать setjmp/longjmp, чтобы получить доступ к вызову только с кодом libjpeg, а затем исключите исключение непосредственно в тело if (setjmp(...)) { ... }, если вы хотите использовать исключения.

Ответ 2

Другой ответ применим здесь. При разматывании стека ничего не получится. Даже не имеет значения, использует ли библиотека какое-то сумасшедшее правило вызова, если это не связано с вашими структурами обработки исключений для реализации С++ (что обычно не является программой C). Никакая реализация С++, о которой я знаю, обнаруживает блок catch, выталкивая фреймы стека (что сделало бы оптимизацию кошмаром), все они поддерживают внутренние структуры для обработки исключений. Пока вызов вниз в цепочке вызовов не испортится с этими структурами, разворачивание стека будет отлично работать для всего вашего личного кода. Теперь, в общем, вполне возможно, что это оставит библиотеку с перепутанным внутренним состоянием, поскольку вы никогда не возвращаете выполнение в библиотеку для очистки, но в случае обратного вызова ошибки libjpeg ожидает, что поток управления не вернется и имеет по-видимому, уже очищены после себя.

В этом случае я бы пошел на это. В общем, я бы только выбросил фатальные исключения из обратного вызова C.

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