相比其餘語言,C++的異常是比較primitive的,沒有語言層面的stack-trace, STL裏的異常類也頗有限. 然而做爲Modern C++,異常仍是必不可少的,雖然有不少人主張pure error code based error handling, 可是這樣會介入control-flow, 也使代碼更加複雜. 然而異常的一個不足在於它的constructor的參數是一個字符串而已,不少時候咱們但願在異常拋出的時候提供一些context information,好比變量的值等等. 最近看facebook的folly C++ 庫獲得一點啓發,因此動手寫了一些utility template functions 來包裝異常拋出,同時提供格式化的異常信息。具體代碼以下函數
template<typename TExcept, typename ... TArgs> void Throw(TArgs&&... rest) { throw TExcept(ToStr(std::forward<TArgs>(rest)...).c_str()); } template<typename TExcept, typename TVal, typename ... TRest> void ThrowOnFalse(TVal&& value, TRest&&... rest) { if (!value) { Throw<TExcept>(std::forward<TRest>(rest)...); } } // enable exception with formatted string message template<typename E> void Throwf(const char* fmt, ...) { char buf[DEFAULT_BUFSIZE]; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof buf, fmt, ap); va_end(ap); throw E(std::string(buf).c_str()); }
一個macro就搞定了,以下rest
#define THROW_FMT(EXCEPTION, fmt, ...) \ Throwf<EXCEPTION>("[Error] %s:%d:%s:\n" fmt "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__ ) #define CHECK_THROW(cond,E) \ if(!(cond)) \ Throwf<E>("[CheckFailure] %s:%d:%s:\n" "Failed Condition: " #cond , __FILE__,__LINE__,__func__ )
另外,定義Custom Exception也是一個很枯燥的事情,不少時候就是繼承std::exception
或者 std::runtime_error
因此寫了一個Macro來簡化自定義異常類的構造code
#define NEW_RT_EXCEPTION(E) class E: public std::runtime_error\ {\ public:\ explicit E(const char* m): runtime_error(m){}\ explicit E(const std::string& m): runtime_error(m){}\ virtual ~E(){}\ }
在以前的代碼中,注意到ToStr()
函數,其實是一個模板函數,用來作 Convert everything to string,是一個受folly啓發寫的一個簡化版本. 其實就是不少函數特化而已,部分代碼以下orm
/// <summary> /// Everything conversion to string /// </summary> template <typename TSrc> std::string ToStr(TSrc src) { std::stringstream ss; ss << src; return ss.str(); } template<> inline std::string ToStr(double src){ char buf[DEFAULT_BUFSIZE]; d2string(buf, sizeof buf, src); return std::string(buf); } // variadic template version template<typename TSrc, typename ... TRest> std::string ToStr(TSrc src, TRest&&... rest) { std::string r = ToStr(src); r += ToStr(std::forward<TRest>(rest)...); return r; }