Как отловить исключение, генерируемое при инициализации статического члена - PullRequest
7 голосов
/ 25 февраля 2010

У меня есть класс со статическим членом:

class MyClass
{
public:
    static const SomeOtherClass myVariable;
};

Который я инициализирую в файле CPP следующим образом:

const SomeOtherClass MyClass::myVariable(SomeFunction());

Проблема в том, что SomeFunction() читает значение из реестра. Если этот раздел реестра не существует, он генерирует исключение. Это приводит к тому, что моя программа взрывается без предоставления пользователю какого-либо полезного вывода ... есть ли способ, которым я могу перехватить исключение, чтобы я мог его записать?

Ответы [ 5 ]

7 голосов
/ 25 февраля 2010

Мне не очень нравятся static члены данных, проблема инициализации стоит на первом месте.

Всякий раз, когда мне нужно выполнить значительную обработку, я обманываю и вместо этого использую локальный static:

class MyClass
{
public:
    static const SomeOtherClass& myVariable();
};

const SomeOtherClass& MyClass::myVariable()
{
  static const SomeOtherClass MyVariable(someOtherFunction());
  return MyVariable;
}

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

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

Если это вызывается из нескольких потоков:

  • если ваш компилятор с этим справится, прекрасно
  • если ваш компилятор этого не делает, вы можете использовать локальное хранилище потоков (в любом случае, это const)
  • вы можете использовать boost::once в библиотеке Boost.Threads
  • , поскольку оно const, вам может быть все равно, если оно будет инициализировано несколько раз, если someOtherFunction не поддерживает параллельное выполнение (остерегайтесь ресурсов)

Рекомендация : используйте static или global создание переменных только для простых объектов (которые не могут выбрасывать), в противном случае используйте local static переменные для задержки выполнения до тех пор, пока вы не сможете перехватить возникающие исключения.

5 голосов
/ 25 февраля 2010

Конечно - обернуть SomeFunction() в функцию вроде:

int static_error;

void SomeFunctionWrapper() { 
    try { 
        SomeFunction();
    }
    catch(...) { // something more specific if possible
        static_error = 1;
    }
}

Затем при входе в main вы захотите проверить static_error != 0 и распечатать соответствующее сообщение об ошибке, если необходимо (к сожалению, вы не можете знать, существует ли еще std::cerr в вашем обработчике исключений, так что если вы хотите чтобы печатать оттуда, вам нужно сделать что-то вроде вывода на основе C FILE *).

5 голосов
/ 25 февраля 2010

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

0 голосов
/ 25 февраля 2010

Вы можете создать класс-оболочку, который задерживает построение объекта. Затем, когда его впервые используют, он сгенерирует, где когда-либо впервые использовал, если конструктор сгенерирует.

Преимущество этого состоит в том, что перед вызовом main () не выполняется много кода, и если вы вообще не используете глобальный объект, он никогда не будет инициализирован.

Код:

#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/thread/once.hpp>
#include <iostream>

const boost::once_flag DEFAULT_ONCE_FLAG = BOOST_ONCE_INIT;
template <typename T>
class DelayedConstruction {
  public:
  DelayedConstruction(boost::function<T* (void) > const & init = &DelayedConstruction::default_initializer ) :
    m_initializer(init), m_flag(DEFAULT_ONCE_FLAG) { }

  T const & operator*() const { 
    boost::call_once(m_flag, boost::bind(&DelayedConstruction::initialize, this) ) ;
    if ( ! m_object )
      throw std::runtime_error("Object could not be initialized") ;
    return *m_object ;
  }
  T const * operator->() const {
    boost::call_once(m_flag, boost::bind(&DelayedConstruction::initialize, this) ) ;
    if ( ! m_object )
      throw std::runtime_error("Object could not be initialized") ;
    return m_object.get() ;
  }
  static T* default_initializer() { return new T; }
  private:
  void initialize() const {
    m_object.reset( m_initializer() ) ;
  }
  boost::function<T* (void) > m_initializer ;
  mutable boost::scoped_ptr<T> m_object ;
  mutable boost::once_flag m_flag ; 
};

struct Foo {
  Foo(int x = 0) : m_x(x) {
    if ( x == 1 ) throw std::runtime_error("Can't be 1") ;
  }
  int m_x ;
} ;

Foo* make_custom_foo() {
  return new Foo(1) ;
}

DelayedConstruction< const Foo > g_myFoo ;
DelayedConstruction< const Foo > g_anotherFoo(&::make_custom_foo) ;

int main() {

  try {
    std::cout << "My Foo: " << g_myFoo->m_x << std::endl ;
    std::cout << "Another Foo: " << g_anotherFoo->m_x << std::endl ;
  } catch ( std::runtime_error const & e ) {
    std::cout << "ERROR: " << e.what() << std::endl ;
  }

  return 0 ;
}

Распечатывает:

My Foo: 0
ERROR: Can't be 1
0 голосов
/ 25 февраля 2010

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

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