我不是在问C++异常是否安全通过C代码传播,也不会在发生这种情况时发生什么.我已经在SO(阅读下面的问题1,2,3)和本FAQ.我问如何继续:
让我说明一下我的想法:
Say libfoo是一个C库,我想在我的barC++程序中使用它.libfoo需要一个foo_callback我必须提供的回调函数.我的回调中使用的函数和方法可能抛出异常,所以我写道:
void my_callback(void)
{
try
{
// Do processing here.
}
catch(...)
{
// Catch anything to prevent an exception reaching C code.
// Fortunately, libfoo provides a foo_error function to
// signal errors and stop processing.
foo_error() ;
}
}
Run Code Online (Sandbox Code Playgroud)
然后我使用我的回调,如下所示:
// The bar program.
int main()
{
// Use libfoo function to set the desired callback
foo_set_callback(&my_callback) ;
// Start processing. This libfoo function uses internally my_callback.
foo_process() ;
// Check for errors
if( foo_ok() )
{
// Hurray !
}
else
{
// Something gone wrong.
// Unfortunately, we lost the exception that caused the error :(
}
}
Run Code Online (Sandbox Code Playgroud)
我要的是能够赶上从抛出的异常my_callback的main功能,而无需异常传播通过libfoo(是的,这是一种量子异常的实验量子隧道通过C代码).
所以我喜欢使用的代码:
void my_callback(void)
{
try
{
// Do processing here.
}
catch(...)
{
// Catch anything to prevent an exception reaching C code.
// We save the exception using (the magic) ExceptionHolder.
ExceptionHolder::Hold() ;
// Call foo_error function to signal errors and stop processing.
foo_error() ;
}
}
// The bar program.
int main()
{
// Use libfoo function to set the desired callback
foo_set_callback(&my_callback) ;
try
{
// Start processing. This libfoo function uses internally my_callback.
foo_process() ;
// Once gone out of the C land, release any hold exception.
ExceptionHolder::Release() ;
}
catch(exception & e)
{
// Something gone wrong.
// Fortunately, we can handle it in some manner.
}
catch( /*something else */ )
{
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
鉴于以下限制:
libfoo源代码封闭,用C语言编写,并以供应商的编译格式提供.在图书馆进行的测试表明,例外不能通过它传播.我无法访问源文件,也无法获得支持异常的编译版本.我的问题:
boost),甚至c ++ 0x解决了?非常感谢任何建议!
编辑:我添加了一个响应与一个异常保持/释放机制的一点实现.欢迎所有评论家或主张.
编辑:您可以使用fungo,它是我在下面描述的想法的更好实现。来自其作者:
fungo是一个 C++ 库,专为我们这些使用尚不支持 std::exception_ptr 的较旧 C++ 实现的人而设计。
换句话说,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 :
} ;
Run Code Online (Sandbox Code Playgroud)
这是一个与类型无关的类。使用模板引入异常类型:
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
} ;
Run Code Online (Sandbox Code Playgroud)
我删除了一堆测试/优化/验证,保留了主要思想。到目前为止,我们已经有了一种类型的异常持有者,因此我们可以构建一个可以同时持有多种类型的异常存储。
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
} ;
Run Code Online (Sandbox Code Playgroud)
我可以使用异常存储,如下所示:
// 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 ...
}
}
Run Code Online (Sandbox Code Playgroud)
这是非常基本的,可能有一些意想不到的场景无法通过此示例处理。AddExceptionHolder如果抛出未使用声明的类型的异常,则有两种可能性:
目前,我更喜欢使用此解决方案,而不是经过更多测试/使用/验证的boost::enable_current_exception,因为我无法重构整个 C++ 代码以用boost::enable_current_exception(...).
不管怎样,这std::exception_ptr似乎是完美的解决方案,一旦我可以转向新的 C++ 标准,我将替换上面的代码。
| 归档时间: |
|
| 查看次数: |
1794 次 |
| 最近记录: |