3

我之前问过一个关于如何在 C++ 中链接异常的问题,其中一个答案提供了一个很好的解决方案来解决这个问题。问题是我不懂代码,试图在评论中进行这种讨论实在是太麻烦了。所以我认为最好完全开始一个新问题。

代码包含在下面,我已经清楚地标记了我没有得到的每个部分。代码下方包含对我不理解的内容的描述。代码由Potatoswatter编写。


代码


struct exception_data { // abstract base class; may contain anything
    virtual ~exception_data() {}
};

struct chained_exception : std::exception {
    chained_exception( std::string const &s, exception_data *d = NULL )
        : data(d), descr(s) {
        try {
            link = new chained_exception;

            // ----------------------------------------------------------------
            //   How does this work (section 1)?
            throw;
            // ----------------------------------------------------------------

        } catch ( chained_exception &prev ) {
            // ----------------------------------------------------------------
            //   How does this work (section 2)?
            swap( *link, prev );
            // ----------------------------------------------------------------
        } // catch std::bad_alloc somehow...
    }

    friend void swap( chained_exception &lhs, chained_exception &rhs ) {
        std::swap( lhs.link, rhs.link );
        std::swap( lhs.data, rhs.data );
        swap( lhs.descr, rhs.descr );
    }

    virtual char const *what() const throw() { return descr.c_str(); }

    virtual ~chained_exception() throw() {
        // --------------------------------------------------------------------
        //   How does this work (section 3)?
        if ( link && link->link ) delete link; // do not delete terminator
        // --------------------------------------------------------------------
        delete data;
    }

    chained_exception *link; // always on heap
    exception_data *data; // always on heap
    std::string descr; // keeps data on heap

private:
    chained_exception() : link(), data() {}
    friend int main();
};

void f() {
    try {
        throw chained_exception( "humbug!" );
    } catch ( std::exception & ) {
        try {
            throw chained_exception( "bah" );
        } catch ( chained_exception &e ) {
            chained_exception *ep = &e;
            for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
                // Print ep->what() to std::cerr
            }
        }
    }

    try {
        throw chained_exception( "meh!" );
    } catch ( chained_exception &e ) {
        for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
            // Print ep->what() to std::cerr
        }
    }
}

int main() try {
    // ------------------------------------------------------------------------
    //   How does this work (section 4)?
    throw chained_exception(); // create dummy end-of-chain
    // ------------------------------------------------------------------------
} catch( chained_exception & ) {
    // body of main goes here
    f();
}

运行代码给出以下输出:

bah
humbug!
meh!

我不明白的

  1. throw;inside try-block:我以前从未见过这个。我认为唯一throw;有效的地方是在catch-block 内重新抛出捕获的内容。那么这有什么作用呢?一些调试显然表明抛出的异常是之前抛出的异常,但那是在一个完全不同的try块内。事实上,它甚至在struct声明之外!

  2. 交换字段:为什么我们需要交换异常字段?仅仅复制指针还不够吗?这是为了防止字段指向的结构过早地从堆中删除吗?

  3. 检查link link链接:我可以理解检查link不是NULL(即使删除NULL指针没有效果),但为什么需要检查link链接?

  4. 抛出虚拟异常:为什么需要这个虚拟?它被抛出,但随后又掉了下来。为什么我们需要它作为链条的末端?

4

1 回答 1

4

Clever code - kudos to potatoswatter on this one. I think that I would have to find some way around the last item though.

  1. throw; rethrows the active exception. It is only valid if a catch block is on the stack. I can't recall where I came across that tidbit at but it was probably on SO in the context of some other question. The bare throw gives us access to the current exception by catching it in the chained_exception constructor. In other words, prev in the constructor is a reference to the exception that we are currently processing.

  2. You are correct here. This prevents double deletion.

  3. The sentinel exception, the one thrown in main, should never be deleted. The one identifying attribute of this exception is that it's link member is NULL.

  4. This is the part that I don't like but cannot think of an easy way around. The only visible chained_exception constructor can only be called when a catch block is active. IIRC, a bare throw without an active catch block is a no-no. So, the workaround is to throw in main and put all of your code in the catch block.

Now, if you try this method in multi-threaded code, make sure that you understand (4) very well. You will have to replicate this in your thread entry point.

于 2010-08-25T14:44:01.077 回答