我并不是问 C++ 异常通过 C 代码传播是否安全,也不是问发生这种情况时会发生什么。我已阅读 SO(1, < a href="https://stackoverflow.com/questions/6426835/what-happens-if-exception-gets- thrown-through-c-code">2,3) 和 此常见问题解答。我问如何继续:
- 避免向 C 代码泄漏任何 C++ 异常(这意味着在调用 C 代码之前捕获 C++ 领域中的所有异常)
- 还能够捕获 C 代码之外的异常(在更高的 C++ 代码中) 。
让我说明一下我的想法:
假设 libfoo
是一个 C 库,我想在我的 bar
C++ 程序中使用它。 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() ;
}
}
然后我使用我的回调,如下所示:
// 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 :(
}
}
我想要的是能够捕获从 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 */ )
{
}
// ...
}
考虑到以下限制:
-
libfoo
是源代码封闭的,用 C 编写,并由供应商以编译格式提供。对库进行的测试表明异常无法通过它传播。我无法访问源文件,也无法获得支持异常的编译版本。
- 回调函数大量使用了使用异常的 C++ 代码。所有的错误处理都是围绕异常机制构建的。我决不能简单地使用吞掉所有异常的代码。
- 不涉及多线程。
- 不支持 c++0x。
我的问题:
- 它是否已经被某些库或某些 C++ 魔法(例如
boost
),甚至 c++0x 解决了?
- 如果没有,我如何编写一个适用于任何异常类型的 ExceptionHolder?我对 C++ 很满意,但我还没有找到一种方法来编写一个可靠且易于使用的 ExceptionHolder,它适用于任何异常类型。
非常感谢您的任何建议!
编辑:我添加了一个响应,其中包含异常保持/释放机制的一些实现。欢迎所有批评或建议。
I'm not asking if it is safe for a C++ exception to propagate through C code, nor what happens when such thing occurs. I have read the following questions in SO(1, 2, 3) and this FAQ. I'm asking how to proceed to :
- Avoid leaking any C++ exception toward C code (this implies catching all exceptions in the C++ land before calling C code)
- Also be able to catch the exceptions outside the C code (in a higher C++ code).
Let me illustrate my idea :
Say libfoo
is a C library, that I want to use in my bar
C++ program. libfoo
needs a callback function foo_callback
that I must provide. The functions and methods used in my callback may throw an exception, so I wrote :
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() ;
}
}
And then I use my callback as shown below :
// 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 :(
}
}
What I want is to be able to catch the exceptions thrown from my_callback
in the main
function, without having the exception propagating through libfoo
(Yes, it's a sort of quantum exception experimenting quantum tunnelling through C code).
So the code I would love to use :
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 */ )
{
}
// ...
}
Given the following constraints :
libfoo
is source-closed, written in C and provided in compiled format from a vendor. Tests conducted on the library showed that exceptions cannot propagate through it. I have no access to the source files nor I can obtain a compiled version that supports exceptions.
- The callback function makes extensive use of C++ code that uses exceptions. All the error handling is built around the exception mechanism. In no way I can simply use the code that swallows all exceptions.
- No multithreading involved.
- No c++0x support.
My questions :
- Is it already solved by some library or some C++ magic (like
boost
), or even c++0x ?
- If not, how can I write an ExceptionHolder that works with any exception type ? I'm confortable with C++ but I haven't found a way to write a reliable and easy to use ExceptionHolder that works with any exception type.
Many thanks for any advice !
EDIT : I added a response with a little implementation of the exception holding/releasing mechanism. All critics or propositions are welcome.
发布评论
评论(4)
我相信 boost.exception 有一种机制可以适应您的目的。请参阅此处获取灵感:
http://www.boost。 org/doc/libs/1_47_0/libs/exception/doc/tutorial_exception_ptr.html
它似乎是为了在线程之间传达它们的特殊异常类型,但我认为它几乎是同一件事 - 阻止异常跨线程边界或 C 代码边界传播,将其副本存储在指针后面,然后稍后通过边界另一侧的指针检索它,并可以选择重新抛出它。
我不确定让你自己的异常从 boost 的神奇异常类型派生有多大可行性,但如果我在大约一年前使用它时没记错的话,这是相当合理的。
I believe that boost.exception has a mechanism that could be adapted to be useful for your purpose. See here for inspiration:
http://www.boost.org/doc/libs/1_47_0/libs/exception/doc/tutorial_exception_ptr.html
It seems to be intended for communicating their special exception type between threads, but I think it is pretty much the same thing - stop an exception from propagating across the thread boundary or C code boundary, stash a copy of it away behind a pointer, then retrieve it later via the pointer on the other side of the boundary and optionally rethrow it.
I'm not sure how feasible it is to make your own exceptions derive from boost's magical exception type, but if I remember correctly from tooling around with it a year or so ago, it's fairly reasonable.
在 c++11 中,您可以在回调中使用 current_exception() 来设置 exception_ptr 类型的变量,然后当库通知您的调用代码有错误时,使用 rethow_exception()。
这与 c++0x 中用于跨线程传播异常的机制相同。
例子:
In c++11 you can use current_exception() in your callback to set a variable of type exception_ptr, and then when your calling code is informed of the error by the library use rethow_exception().
This is the same mechanism used to propagate exceptions across threads in c++0x.
Example:
编辑:您可以使用 fungo,这是我在下面描述的想法的更好实现。来自其作者:
我会将其标记为答案。
正如我在问题中提到的,我现在无法使用 C++0x/11 功能(目前没有计划使用新功能),我将在这里介绍我到目前为止所做的事情:
异常的生命周期跨越try-catcher 块。为了保存异常,必须在堆上创建一个副本。当重新抛出异常时,我们会删除副本。我编写了一个异常持有者接口:
这是一个类型独立的类。异常类型是使用模板引入的:
我删除了一堆测试/优化/验证,我保留了主要思想。到目前为止,我们已经有了一种类型的异常持有者,因此我们可以构建一个可以同时持有多种类型的异常存储。
我可以使用异常存储,如下所示:
这是非常基本的,并且可能存在一些本示例未处理的意外场景。如果抛出未使用
AddExceptionHolder
声明的类型的异常,则有两种可能性:目前,我更喜欢使用此解决方案,而不是经过更多测试/使用/验证的 boost::enable_current_exception 因为我无法重构整个 C++ 代码来包围所有抛出站点
boost::enable_current_exception(...)
。不管怎样,std::exception_ptr 似乎是完美的解决方案,一旦我可以转向新的 C++ 标准,我将替换上面的代码。
Edit : You can use fungo, a better implementation of the idea I described below. From its author :
I'll mark this as an answer.
As I mentioned in my question, I cannot use C++0x/11 features for now (using new features isn't planned for now), and I will present here what I have done so far :
Exceptions have a lifetime that spans across the try-catcher block. In order to save an exception, one must create a copy on the heap. We get rid of the copy when re-throwing the exception. I wrote an exception holder interface :
This is a type independent class. The exception type is introduced using templates :
I removed a bunch of tests/optimizations/verifications, I kept the main idea. So far we have an exception holder for one type, so we can build an exception store that can hold many types at a time.
I can use the exception store as shown below :
This is very basic, and there might be some unexpected scenarii that are not handled by this example. If an exception with a type not declared using
AddExceptionHolder
is throw, you have two possibilities :For now, I prefer using this solution to the more tested/used/verified boost::enable_current_exception because I can't afford refactoring the whole C++ code to surround all throw sites with
boost::enable_current_exception(...)
.Anyway, the
std::exception_ptr
seems to be the perfect solution, and I will replace the above code once I can move to the new C++ standard.如果您不准备进行重大重构,您是否能够识别可能引发的异常类型的特定子集?
如果是这样,您可能可以编写一个容器来专门存储每个副本,然后在从 C 调用返回时检索它们。这需要能够复制异常类型。
或者,考虑各种异常的最终解决方案是什么(如果可以的话),并将其打包到某个对象中以便在返回时进行处理。仅当您拥有足够的代码库以了解可能会产生哪些潜在的异常处理时,这才有效。
Hydrid 模型会捕获异常,根据发现的内容创建一个新的异常,然后在从 C 库返回时抛出该异常。
If you are not up for a major refactor, are you able to identify a specific subset of exception types that might be thrown?
If so, you can probably write a container to store copies of each of these specifically, and then retrieve them on return from the C-call. This requires being able to copy the exception type.
Alternatively, consider what the ultimate resolution of the various exceptions is (if you can), and pack that up into some object for processing on return. This only works if you own enough of the codebase to know what potential handling the exceptions may generate.
A hydrid model would trap the exceptions, create a new exception based on what was found, and then throw that on return from the C lib.