一個 boost::exception 的例子

因為覺得 dprintf 出現的次數實在太多了,我會希望在 exception 就可以包好 __FILE__ 跟 __LINE__,又可以把 std::wstring 包在 exception 一起丟出去,在 catch 的時候再印出來就好。

所以試著寫了這樣一個例子,結果寫的四不像,跟原本想的東西完全部不同了,而且我對於那個 BUFSIZ 相當不滿啊。其實原本希望可以用 ##__VA_ARGS__ 這種 macro 直接吃進來變成一個 boost::any[],然後再想辦法轉成 boost::format 可以用的格式,中間還可以透過 BOOST_FOREACH 來玩的,不過我徹底失敗了,所以最後的成果大家笑笑就好了。

#define BOOST_THROW_EXCEPTION_STRING(fmt, ...)\
    do {\
        BOOST_THROW_EXCEPTION(an_exception() << throw_wstring(valistToStr(fmt, ##__VA_ARGS__)));\
        \
    } while(0)

inline std::wstring valistToStr(const wchar_t *fmt, ...)
{
    wchar_t wszMessage[BUFSIZ] = {0};

    va_list ap;
    va_start(ap, fmt);
    vswprintf(wszMessage, sizeof(wszMessage)/sizeof(wszMessage[0]) - 1, fmt, ap);
    va_end(ap);

    return std::wstring(wszMessage);
}

typedef boost::error_info< struct tag_throw_wstring, std::wstring > throw_wstring;
struct an_exception : public boost::exception, public std::exception {};

int main(int argc, wchar_t *argv[])
{
    try
    {
        BOOST_THROW_EXCEPTION_STRING( L"%S%d", L"lala", 1);
    }
    catch(boost::exception& e)
    {
        boost::wformat fmt(L"[%S] %S - [%S(%d)]");
        std::wstring wstrError = boost::str(fmt %
                *boost::get_error_info<boost::throw_function>(e) %
                boost::get_error_info<throw_wstring>(e)->c_str() %
                *boost::get_error_info<boost::throw_file>(e) %
                *boost::get_error_info<boost::throw_line>(e)
        );

        std::wcout << wstrError << std::endl;
    }

    return 0;
}

然後關於 boost::any 的 construct 不能吃 const char * 的事情,其實不用改到 boost::any 的 source code,稍微這樣惡搞一下就可以了。

class my_any : public boost::any
{
public:
    template<typename ValueType>
    my_any(const ValueType & value)
    {
        typename boost::decay<const ValueType>::type t = value;
        boost::any a(t);
        this->swap(a);
    }
};

不過我大概要好一段時間碰不到 C++ 了,可能有幾個月要轉換開發平台了(默)

4 Replies to “一個 boost::exception 的例子”

  1. 我做過類似的事,不知道是不是你要的方法。我的做法不會用到 va_list,也不需要再定一個 wchar_t wszMessage[BUFSIZ].
    我是用 function overload template 來做的,像這樣:
    template
    void BOOST_THROW_EXCEPTION_STRING(const wchar_t *fmt, const T1 &t1)
    { throw exception with a string from (boost::format(fmt) % t1).str() }

    template
    void BOOST_THROW_EXCEPTION_STRING(const wchar_t *fmt, const T1 &t1, const T2 &t2)
    { throw exception with a string from (boost::format(fmt) % t1 % t2).str() }

    從 0 個參數的版本搞到 20 個參數的版本應該就夠用了。
    我在這邊的留言也有提到 http://fsfoundry.org/codefreak/2009/05/04/ostream-type-preservation/

  2. ah, 該死,template 的尖角參數又被吃掉了…重貼一次
    template<typename T1>
    void BOOST_THROW_EXCEPTION_STRING(const wchar_t *fmt, const T1 &t1)
    { throw exception with a string from (boost::format(fmt) % t1).str() }

    template<typename T1, T2>
    void BOOST_THROW_EXCEPTION_STRING(const wchar_t *fmt, const T1 &t1, const T2 &t2)
    { throw exception with a string from (boost::format(fmt) % t1 % t2).str() }

  3. 謝謝你的回覆 😀

    其實我也有想到類似的作法,只是當天早上覺得幾個參數就要 overload 幾次,覺得好像跟我的初衷有點不同。不過後來仔細想了想,其實似乎某些 library 也會限制參數的個數來讓可用性更好吧。也許下次我就直接寫寫看好了,比起用 va_list 跟設定一個固定大小的 buffer,我還寧願多 overload 幾次勒 😀

Leave a Reply

Your email address will not be published. Required fields are marked *