分享自定義靜態斷言程式碼
阿新 • • 發佈:2018-12-16
本文程式碼來自於QQ群中nous大神,類似於static assert,不過斷言異常內容可以自定義。
程式碼分為boost版本和標準版本,使用ENSURE_HAS_BOOST 巨集來區分。
程式碼:
// // ensure.hpp // // Copyright (c) Nous Xiong. // // Macro ENSURE impl. // #ifndef ENSURE_HPP #define ENSURE_HPP #ifdef ENSURE_HAS_BOOST #include <boost/exception/all> #include <boost/thread/tss.hpp> #include <boost/preprocessor/seq/for_each.hpp> #include <boost/integer.hpp> #include <stdexcept> #include <sstream> #include <iostream> namespace un { /// 執行時刻資訊 typedef boost::error_info<struct tag_runtime, std::string> errinfo_runtime_t; /// 專用於ensure的異常資訊 typedef boost::error_info<struct tag_ensure, std::string> errinfo_ensure_t; /// 異常基類 class exception : public virtual std::exception , public virtual boost::exception { }; class ensure { public: ensure() : current_function_(0) , file_(0) , line_(-1) , msg_(0) { } ~ensure() { } public: ensure& set_context(char const* expr, char const* current_function, char const* file, int line) { msg_ = 0; err_.str(""); err_ << "Ensure failed, expression: '" << expr << "', values: "; current_function_ = current_function; file_ = file; line_ = line; // 這裡你可以將std::cerr替換為你自己專案的日誌系統 std::cerr << "Ensure failed point: [" << file_ << "]=[" << line_ << "]" << std::endl; return *this; } ensure& set_current_val(boost::int8_t src, char const* name) { boost::int32_t tmp = (boost::int32_t)src; return set_val(tmp, name); } ensure& set_current_val(bool src, char const* name) { return set_val(src, name); } ensure& set_current_val(boost::int16_t src, char const* name) { return set_val(src, name); } ensure& set_current_val(boost::uint16_t src, char const* name) { return set_val(src, name); } ensure& set_current_val(boost::int32_t src, char const* name) { return set_val(src, name); } ensure& set_current_val(boost::uint32_t src, char const* name) { return set_val(src, name); } ensure& set_current_val(boost::int64_t src, char const* name) { return set_val(src, name); } ensure& set_current_val(boost::uint64_t src, char const* name) { return set_val(src, name); } ensure& set_current_val(std::string const& src, char const* name) { err_ << name << " = " << src << ", size: " << src.size() << "; "; return *this; } // 你可以加入任何你專案需要的資料型別 // 例如: //ensure& set_current_val(my_data const& src, char const* name) //{ // err_ << name << " = " << src.get_xxx() << "; "; // return *this; //} /// 使用者可新增的訊息 ensure& set_current_val(char const* msg, char const*) { msg_ = msg; return *this; } /// 丟擲異常 template <typename ExceptT> ensure& set_current_val(ExceptT const& ex, char const*) { if (msg_) { ex << errinfo_runtime_t(msg_); } ex << errinfo_ensure_t(err_.str()); boost::exception_detail::throw_exception_(ex, current_function_, file_, line_); return *this; } static ensure& get_ensure() { // 為了多執行緒下方便的使用ensure,這裡使用了執行緒本地儲存, // 如果是單執行緒程式,這裡可以直接換為static ensure e; static boost::thread_specific_ptr<ensure> this_ens; ensure* ret = this_ens.get(); if (!ret) { this_ens.reset(new ensure); ret = this_ens.get(); } return *ret; } private: template <typename T> ensure& set_val(T const t, char const* name) { err_ << name << " = " << t << "; "; return *this; } private: std::stringstream err_; char const* current_function_; char const* file_; int line_; char const* msg_; }; } #define ENSURE_OP_IMPL(e, elem) e.set_current_val((elem), #elem); // 必須要定義這個巨集,不然#elem無法正確的顯示變數的字串形式 #define ENSURE_OP(COLA_ENS, e, elem) ENSURE_OP_IMPL(e, elem) #define ENSURE(expr, args) \ if( (expr) ) ; \ else \ { \ un::ensure& e = un::ensure::get_ensure(); \ e.set_context(#expr, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__); \ BOOST_PP_SEQ_FOR_EACH(ENSURE_OP, e, args); \ } #else #include <stdexcept> #include <sstream> #include <iostream> namespace un { class ensure { public: ensure() : ENSURE_A(*this) , ENSURE_B(*this) , file_(0) , line_(-1) , msg_(0) { } ~ensure() { } ensure& ENSURE_A; ensure& ENSURE_B; public: ensure& set_context(char const* expr, char const* file, int line) { msg_ = 0; err_.str(""); err_ << "Ensure failed, expression: '" << expr << "', values: "; file_ = file; line_ = line; // 這裡你可以將std::cerr替換為你自己專案的日誌系統 std::cerr << "Ensure failed point: [" << file_ << "]=[" << line_ << "]" << std::endl; return *this; } ensure& set_current_val(char src, char const* name) { int tmp = (int)src; return set_val(tmp, name); } ensure& set_current_val(bool src, char const* name) { return set_val(src, name); } ensure& set_current_val(short src, char const* name) { return set_val(src, name); } ensure& set_current_val(int src, char const* name) { return set_val(src, name); } ensure& set_current_val(std::string const& src, char const* name) { err_ << name << " = " << src << ", size: " << src.size() << "; "; return *this; } // 你可以加入任何你專案需要的資料型別 // 例如: //ensure& set_current_val(my_data const& src, char const* name) //{ // err_ << name << " = " << src.get_xxx() << "; "; // return *this; //} /// 使用者可新增的訊息 ensure& set_current_val(char const* msg, char const*) { msg_ = msg; return *this; } /// 丟擲異常 template <typename ExceptT> ensure& set_current_val(ExceptT const& ex, char const*) { if (msg_) { err_ << "user msg = " << msg_; } ex.set_error(err_.str()); // 需要使用者實現這個方法 throw ex; return *this; } static ensure& get_ensure() { // 僅僅單執行緒 static ensure ret; return ret; } private: template <typename T> ensure& set_val(T const t, char const* name) { err_ << name << " = " << t << "; "; return *this; } private: std::stringstream err_; char const* file_; int line_; char const* msg_; }; } #define ENSURE_A(x) ENSURE_OP(x, B) #define ENSURE_B(x) ENSURE_OP(x, A) #define ENSURE_OP(x, next) \ ENSURE_A.set_current_val((x), #x).ENSURE_##next #define ENSURE(expr) \ if( (expr) ) ; \ else un::ensure::get_ensure().set_context(#expr,__FILE__,__LINE__).ENSURE_A #endif #endif /* COLA_ENSURE_HPP */
測試程式碼:
// ensure.cpp : Defines the entry point for the console application. // #include "ensure.hpp" #ifdef ENSURE_HAS_BOOST class my_except : public virtual un::exception {}; int main(int argc, char *argv[]) { try { boost::int32_t i = 0; std::string str("test str"); ENSURE(i == 0 && str.empty(), (i)(str)("Test Ensure Msg")(my_except())); std::cout << "Shouldn't be here." << std::endl; } catch (my_except& ex) { std::cerr << boost::diagnostic_information(ex) << std::endl; } return 0; } #else class my_except : public virtual std::exception { public: void set_error(std::string const& err) const { err_ = err; } virtual char const* what() const throw() { return err_.c_str(); } private: mutable std::string err_; }; int main(int argc, char *argv[]) { try { int i = 0; std::string str("test str"); ENSURE(i == 0 && str.empty())(i)(str)("Test Ensure Msg")(my_except()); std::cout << "Shouldn't be here." << std::endl; } catch (my_except& ex) { std::cerr << ex.what() << std::endl; } system("pause"); return 0; } #endif
ensure下載地址: