Создание / отлов исключений из C'tor статического объекта в C ++ - PullRequest
3 голосов
/ 17 февраля 2009

У меня есть случай, когда мне нужно прочитать входной файл в C'tor, но иногда этот файл не существует. Этот объект обычно хранится статически, поэтому его C'tor вызывается при загрузке DLL. Я не могу поймать исключение, которое я выбрасываю, если файл не существует, потому что он слишком ранний, и мой исполняемый файл вылетает ужасно. Я знаю, что выкидывать исключения из C'or - это плохая практика, но я не могу продолжать, если файл не существует, поэтому я должен это сделать. Есть ли способ перехватить исключение при загрузке DLL, и если нет, есть ли лучшее решение для этого сценария?

Спасибо, Gal

Ответы [ 7 ]

8 голосов
/ 17 февраля 2009

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

class Object;
Object& getObject()
{
    static Object object;
    return object;
}

Статический экземпляр Object будет инициализирован при первом вызове метода getObject () . Если выдает конструктор Object , вы можете легко перехватить исключение. Вам просто нужно помнить, что каждый вызов getObject () следует помещать в блок try / catch (или вы столкнетесь с исключением, переполняющим цепочку стека); это может быть немного неудобно, но, с другой стороны, вы можете решить обернуть только логически «первый» вызов, если вы знаете, какой из них находится в потоке логики программы.

3 голосов
/ 17 февраля 2009

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

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

2 голосов
/ 29 февраля 2016

Если вы можете использовать c ++ 11, тогда есть лямбда и unique_ptr<> решение для этого:

// In some_file.hpp
#pragma once
#include <memory>
#include <stdexcept>

class CtorThrows {
public:
  CtorThrows (int value) {
    if (value < 10) {
      throw std::runtime_error("oops!");
    }
  }
};

class ClassWithStatic {
public:
private:
  static std::unique_ptr<CtorThrows> bad_member_; // <-- static member
};

, а затем

// In some_file.cpp
#include "some_file.hpp"

// Create a lambda function to initialize the static member variable.
std::unique_ptr<CtorThrows> ClassWithStatic::bad_member_ = []() {
  try {
    int value = 5;  // in this case, it is a bad value

    // This only returns successfully if bad_value DOESN'T cause
    // the ctor to throw and exception.
    return std::make_unique<CtorThrows>(value);
  } catch (std::runtime_error &e) {
    std::cerr << "OOPs!  Here's a nice error message" << std::endl;
    exit(1);
  }
  return std::unique_ptr<CtorThrows>(nullptr);
}();

Использование unique_ptr позволяет вам делать это даже с классами, в которых есть конструктор удаленного или закрытого копирования и оператор присваивания копии.

1 голос
/ 17 февраля 2009

Вы правы, что не можете отловить исключения, которые происходят во время инициализации статических объектов.

Поскольку вы пишете DLL: каждая DLL может иметь точку входа, и внутри этой точки входа работает обработка исключений. (Это так же, как основной в вашей основной программе). Я бы удалил статические экземпляры ваших классов, заменил их указателями и инициализировал эти указатели внутри dllmain.

Это решит твои проблемы раз и навсегда.

Кстати - точка входа в DLL вызывается при загрузке, выгрузке и других событиях, таких как присоединение / отключение процесса. Убедитесь, что вы используете правильное место для инициализации своих классов.

1 голос
/ 17 февраля 2009

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

1 голос
/ 17 февраля 2009

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

В качестве альтернативы замените статический объект статическим указателем и при необходимости вызовите новый. Лучше использовать умный указатель, такой как auto_ptr.

0 голосов
/ 17 февраля 2009

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

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