Как поймать исключение, выданное при инициализации статического участника

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

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

Как который я инициализирую в файле CPP так:

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

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

7
задан rmeador 24 February 2010 в 22:27
поделиться

5 ответов

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

Всякий раз, когда мне приходится выполнять значительную обработку, я иду на хитрость и использую вместо него локальный 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 не поддерживает параллельное выполнение (остерегайтесь ресурсов)

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

7
ответ дан 6 December 2019 в 11:48
поделиться

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

5
ответ дан 6 December 2019 в 11:48
поделиться

Конечно - заключите 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
ответ дан 6 December 2019 в 11:48
поделиться

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

0
ответ дан 6 December 2019 в 11:48
поделиться

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

Это дает преимущество в том, что перед вызовом 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
ответ дан 6 December 2019 в 11:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: