TrickDiary このページをアンテナに追加 RSSフィード Twitter

2008-12-31

[] C++0x の nested_exception について 其の参(考察)

前々回前回C++0x の nested_exception について記述してきましたが、いよいよ本題です。

具体的な木構造例外処理のコードを C++0x の nested_exception を使って記述したものと trickerr.h を使って記述したものがそれぞれ以下のようになります。

nested_exception による木構造例外処理

void A()
{
    try
    {
        std::vector<exception_ptr> exception_list;
        try
        {
            // A() と同じような構造を持つ処理
            A_a();
        }
        catch(...)
        {
            exception_list.push_back(current_exception());
            //  通常、例外オブジェクトはcatch句を抜ける時に破棄される(旧§15.1¶4)が
            //  新§18.7.5¶7により、補足された例外オブジェクトは exception_ptr で
            //  参照されている間は有効。
        }
        
        // A_a() が失敗した場合でも A() でやらなければならない処理。
        try
        {
            // A() と同じような構造を持つ処理
            A_b();
        }
        catch(...)
        {
            exception_list.push_back(current_exception());
        }
        if (!exception_list.empty())
        {
            throw exception_list;
        }
    }
    catch(...)
    {
        throw_with_nested(A_exception("someone error"));
    }
}
void print_tree_exception(exception_ptr e, const std::string & indent = "")
{
    const char * indent_unit = " ";
    const char * mark = "- ";
    try
    {
        rethow_exception(e);
    }
    catch(const std::vector<exception_ptr> e)
    {
        for(std::vector<exception_ptr>::const_iterator i = e.begin(); i != e.end(); ++i)
        {
            print_tree_exception(i, indent);
        }
    }
    catch(const std::nested_exception  e)
    {
        print_tree_exception(evil_i(e), indent +indent_unit);
    }
    catch(const std::exception e)
    {
        std::cout << indent << mark << e.what() << std::endl;
    }
    catch(...)
    {
        std::cout << indent << mark << "unknown exception" << std::endl;
    }
}
int main(int, char * [])
{
    try
    {
        A();
    }
    catch()
    {
        print_tree_exception(current_exception());
    }
    return EXIT_SUCCESS;
}

trickerr.h による木構造例外処理

void A()
{
    tricklib::error_listener_type error_listener;
    A_a();
    A_b();
    if (error_listener.has_error()) // この if ブロックは error_listener_type を継承したクラスのデストラクタに記述することも可能。
    {
        throw_error(new A_error("someone error", error_listener.listener_off().extract_pending_error()));
    }
}
void print_tree_error(const tricklib::error_type &a_error, const std::string & indent = "")
{
    const char * indent_unit = " ";
    const char * mark = "- ";
    
    tricklib::error_type error = a_error;
    while(error)
    {
        std::cout << indent << mark << error->message << std::endl;
        if (error->children)
        {
            print_tree_error(error->children, indent +indent_unit);
        }
        error = error->next;
    }
}
int main(int, char * [])
{
    tricklib::error_thread_power error_thread_power_on; // スレッド毎に必要。
    
    try
    {
        A();
    }
    catch(error_type error)
    {
        print_tree_error(error);
    }
    catch(...)
    {
        std::cout << "- unknown exception" << std::endl;
    }
    return EXIT_SUCCESS;
}

考察

私が書いたコードがよくないだけでもっとマシな nested_exception による木構造例外処理の記述方法があるかもしれませんが、いま手元に無い情報を前提にもできませんので上のコードで話を進めますが、よりよいコードを思いついた方はその旨お知らせ願います。

評価のポイントとしては print_tree_exception()/print_tree_error() および main() はひとつあればよい類のものですので、特に注視すべきは A() となり...

  • nested_exception の例ではそもそも nested_exception を使う必然性がほとんどない。( exception_list をそのまま投げっぱなしすれば、その分簡素な記述で済みます。 )
  • nested_exception の例では try〜throw〜catch の仕組みが木構造例外処理を前提としていないのでどうしても冗長な記述になる。( コメントのせいで余分に冗長に見えますが、例外が発生する可能性がある下位の処理(関数)をひとつ呼び出す度に try〜catch の記述が必要になってしまうのが問題の本質です。)

...といったところがポイントかと思います。

以上のことから nested_exception で木構造例外処理というのは不可能ではないとしても無理のある話であり、単体の例外情報ではなく複数の例外情報を扱いたい場面で果たして木構造例外にまともに対応していない nested_exception で用をなせるかという点については疑問を持たざるを得えません。

§18.7.5 例外伝播由来の exception_ptr や current_exception() などはそのままでよいと思いますが、 nested_exception は木構造例外処理に対応する為の修正を施すか、そうしないのであれば§18.7.6 nested_exception は廃案にしたほうがよいのではないかと思います。