Редактировать: Вы можете использовать fungo , лучшую реализацию идеи, которую я описал ниже.От ее автора:
fungo - это библиотека C ++, предназначенная для тех из нас, кто застрял в старых реализациях C ++, которые еще не поддерживают std :: exception_ptr.
Другими словами, fungo позволяет вам честно попытаться сохранить, а затем перезапустить исключения, перехваченные блоком catch (...).Это полезно для распространения исключений через соединения потоков или через граничные интерфейсы C / C ++.
Я отмечу это как ответ.
Как я уже упоминал в моемвопрос, я не могу сейчас использовать функции C ++ 0x / 11 (использование новых функций пока не планируется), и я представлю здесь то, что я сделал до сих пор:
Исключения имеют время жизни, которое охватываетчерез блок try-catcher.Чтобы сохранить исключение, необходимо создать копию в куче.Мы избавляемся от копии при повторном отбрасывании исключения.Я написал держатель исключений интерфейс:
class ExceptionHolderInterface
{
public :
ExceptionHolderInterface(void) ;
virtual ~ExceptionHolderInterface(void) ;
/* For holding an exception. To be called inside a catch block.*/
virtual void Hold(void) = 0 ;
/* For releasing an exception. To be called inside a try block.*/
virtual void Release(void) = 0 ;
private :
} ;
Это класс, независимый от типа.Тип исключения вводится с использованием шаблонов:
template<typename ExceptionType>
class ExceptionHolder : public ExceptionHolderInterface
{
public :
ExceptionHolder(void) ;
virtual ~ExceptionHolder(void) ;
virtual void Hold(void)
{
try
{
throw ;
}
catch(const ExceptionType & e)
{
exception.reset(new ExceptionType(e)) ;
}
}
virtual void Release(void)
{
if(exception.get())
{
throw ExceptionType(*exception.get()) ;
}
}
private :
std::auto_ptr<ExceptionType> exception ;
// declare the copy-constructor and the assignment operator here to make the class non-copyable
} ;
Я удалил кучу тестов / оптимизаций / проверок, сохранил основную идею.Пока у нас есть держатель исключений для одного типа, поэтому мы можем создать хранилище исключений , которое может хранить много типов одновременно.
class ExceptionStore
{
public :
ExceptionStore(void) ;
~ExceptionStore(void)
{
for(Iterator holder = exception_holders.begin() ; holder != exception_holders.end() ; ++holder)
{
delete (*holder) ;
}
}
// Add an exception type to handle
template<typename ExceptionType>
void AddExceptionHolder(void)
{
exception_holders.push_back(new ExceptionHolder<ExceptionType>()) ;
}
// Try to hold an exception using available holders. Use this inside a catch block.
void Hold(void)
{
Iterator holder = exception_holders.begin() :
while(holder != exception_holders.end())
{
try
{
(*holder)->Hold() ;
break ;
}
catch(...)
{
++holder ;
}
}
}
// Try to release any hold exception. Call this inside a try-block.
void Release(void)
{
Iterator holder = exception_holders.begin() :
while(holder != exception_holders.end())
{
(*holder++)->Release() ;
}
}
private :
std::list<ExceptionHolderInterface *> exception_holders ;
typedef std::list<ExceptionHolderInterface *>::iterator Iterator ;
// Declare the copy-constructor and the assignment operator here to make the class non-copyable
} ;
Я могу использовать хранилище исключений, как показано ниже:
// I made a global ExceptionStore just to keep the example simple.
ExceptionStore exception_store ;
void callable_from_c_code(void)
{
// Normally, we should retrieve the exception store in some manner.
try
{
// Do processing here. Exceptions may be thrown.
}
catch(...)
{
// Something wrong happened. Let's store the error for later.
exception_store.Hold() ;
}
// Exceptions do not propagate to C code.
}
int main(int, char * [])
{
// First, set the exception types we want to handle. The handling is done in
// the same order as below.
exception_store.AddExceptionHolder<std::runtime_error>() ;
exception_store.AddExceptionHolder<std::logic_error>() ;
exception_store.AddExceptionHolder<MyFancyException>() ;
// Somehow invoke some C code that uses `callable_from_c_code`
use_some_c_library_with_callback(&callable_from_c_code) ;
// Handle any caught exception
try
{
exception_holder.Release() ;
}
catch(std::exception &)
{
// Something gone wrong ...
}
catch(MyFancyException &)
{
// Nothing fancy despite the name. We have problems here ...
}
}
Это очень простой способ, и могут быть некоторые неожиданные сценарии, которые не обрабатываются в этом примере.Если исключение с типом, не объявленным с использованием AddExceptionHolder
, является throw, у вас есть две возможности:
- В хранилище добавляется держатель базового типа, поэтому исключение будет поймано и разрезано,сохраняя только копию базового типа.
- Ни один держатель не подходит для исключения, и он просто теряется.Ничто не просачивается в землю C.
Пока я предпочитаю использовать это решение более проверенному / используемому / проверенному boost :: enable_current_exception , потому что я не могу позволить себе рефакторингвесь код C ++, чтобы окружить все сайты бросков boost::enable_current_exception(...)
.
В любом случае, std::exception_ptr
кажется идеальным решением, и я заменю приведенный выше код, как только смогу перейти на новый стандарт C ++.