什么是“异常喷吐”?

12
最近在CodeReview.SE上,我看到了一个回答,讲述了一种称为“异常喷吐”的技术。显然,这个技巧用于利用异常必须以线程安全的方式实现,而与编译器是否支持thread_local变量无关。
我在下面粘贴了部分回答内容:

There's an existing technique which is not dissimilar referred to as "exception vomiting". Observe:

void f(void(*p)()) {
    p();
}
template<typename F> void real_f(F func) {
    try {
        throw func;
    } catch(...) {
        f([] {
            try {
                throw;
            } catch(F func) {
                func();
            }
        });
    }
}

This abuses the fact that the compiler must provide a thread-local stack for complex objects for use as exception storage, regardless of their support for other thread local features, and therefore enjoys much broad compiler support. The most obvious drawbacks are a) it's horrible, and b) it's limited to stack semantics.

我的问题是,这个技巧实际上是如何工作的,并且它是否“安全”?


3
按值传递有点让我担心,或者这整个事情超出了我的能力范围? - Bathsheba
@Bathsheba 这不是我的代码,我也不知道它是如何工作的。这就是为什么我在这里问的原因 ;) - Henri Menke
只是我的个人看法,但我的理解是C++标准直到C++11才引入了"thread_local"关键字并开始正式承认线程的存在和引用线程安全,之前并没有直接承认。因此,我认为如果假设这种技术可行,那么它只能在非常少数的编译器中有用吧? - Thomas Russell
@ThomasRussell - 在这里使用lambda使得任何C++11之前的内容都变得无关紧要。 - StoryTeller - Unslander Monica
3
代码审查说:“它可以用作提供捕获lambda作为C回调函数:”,接着是一些代码。这里的代码回答说:“这滥用了编译器必须为复杂对象提供线程本地堆栈以用作异常存储的事实,而不管它们是否支持其他线程本地功能,因此得到了广泛的编译器支持。最明显的缺点是a)它很糟糕,b)它仅限于堆栈语义。” 我认为如果问题包括这样的背景信息会更清晰明了。 - 463035818_is_not_a_number
显示剩余2条评论
1个回答

6

这种技术依赖于异常必须以线程安全的方式实现,以便在多线程应用程序中使用异常。即使是C++-11之前的编译器,在线程成为C++标准的一部分之前就已经支持了线程安全的异常。

每个线程通过使用特定于线程的存储来独立throw/catch异常,以避免与其他线程发生冲突。没有参数的throw重新抛出存储在该线程特定存储中的当前异常。此异常存储用于存储函数及其捕获的参数(有状态的lambda或任何其他可调用对象)。

这种技术的缺点是,抛出异常通常涉及内存分配,因此增加了new/delete调用的开销。


另一种实现方式是使用一个不可移植但广泛支持的__thread存储说明符。这可以避免动态内存分配的开销:

void f(void(*p)()) { // The C-style function.
    p();
}

__thread void(*function)();

template<class Function>
void adapter() {
    (*reinterpret_cast<Function*>(function))();
}

template<typename F>
void invoke_f(F const& func) {
    function = reinterpret_cast<void(*)()>(&func);
    f(adapter<F const>);
}

int main(int ac, char**) {
    invoke_f([ac]{ std::cout << ac << '\n'; });
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接