1. 程式人生 > >分享自定義靜態斷言程式碼

分享自定義靜態斷言程式碼

本文程式碼來自於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下載地址: