У меня есть класс со статическим участником:
class MyClass
{
public:
static const SomeOtherClass myVariable;
};
Как который я инициализирую в файле CPP так:
const SomeOtherClass MyClass::myVariable(SomeFunction());
Проблема, SomeFunction()
читает значение из реестра. Если тот ключ реестра не существует, он выдает исключение. Это заставляет мою программу взрываться, не давая пользователю, полезный вывод... является там некоторым способом, которым я могу поймать исключение, таким образом, я могу зарегистрировать его?
Мне не очень нравятся статические
члены данных, прежде всего из-за проблемы инициализации.
Всякий раз, когда мне приходится выполнять значительную обработку, я иду на хитрость и использую вместо него локальный static
:
class MyClass
{
public:
static const SomeOtherClass& myVariable();
};
const SomeOtherClass& MyClass::myVariable()
{
static const SomeOtherClass MyVariable(someOtherFunction());
return MyVariable;
}
Таким образом, исключение будет выброшено только при первом использовании, и все же объект будет const
.
Это довольно мощная идиома для задержки выполнения. Она имеет небольшие накладные расходы (в основном компилятор проверяет флаг при каждом входе в метод), но лучше сначала побеспокоиться о корректности ;)
Если это вызывается из нескольких потоков:
boost::once
в Boost. Threads
библиотекиconst
, вы можете не беспокоиться о том, что она инициализируется несколько раз, если только someOtherFunction
не поддерживает параллельное выполнение (остерегайтесь ресурсов)Guideline: используйте инстанцирование статических
или глобальных
переменных только для простых объектов (которые не могут бросать), в противном случае используйте локальные статические
переменные для задержки выполнения, пока вы не сможете поймать возникающие исключения.
Возможно, лучше всего было бы добавить раздел реестра в список, а не искать его, а затем, как только будет введена функция main (), пройдите и найдите все ключи в списке. Я не хочу проповедовать, но именно в таких ситуациях, как правило, делать значительную обработку перед вводом main () - плохая идея.
Конечно - заключите 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 * вывод на основе).
Вы можете обернуть функцию внутри другой функции, которая поймает исключение и предупредит пользователя о проблеме (или создаст ключ с безопасным значением по умолчанию)
Вы можете создать класс-оболочку, который задерживает создание объекта. Затем, когда он впервые используется, он будет бросать там, где когда-либо использовался впервые, если конструктор бросает.
Это дает преимущество в том, что перед вызовом 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