Boost源码剖析--<boost/assert.hpp>

Boost源码剖析--<boost/assert.hpp>

By 马冬亮(凝霜  Loki)

一个人的战争(http://blog.csdn.net/MDL13412)

头文件:
        <boost/assert.hpp>

定位:
        BOOST_ASSERT类似于标准库中的assert(定义在<cassert>),目的是在Boost库和用户代码中都可以使用。
分析:
        默认情况下BOOST_ASSERT(expr)等价于assert(expr),但是如果在#include <boost/assert.hpp>前定义BOOST_DISABLE_ASSERTS,那么BOOST_ASSERT(expr)就被实现为((void)0),即不做任何事情,编译器可以很容易的将其优化掉。这样做的优势在于,用户编写代码的时候,可以在不影响assert的前提下,开启和禁用一些断言,为代码提供了更多灵活性。
        如果定义了BOOST_ENABLE_ASSERT_HANDLER,Boost库为用户提供了一个用于断言出错时的回调函数assertion_failed,需要用户自己去实现。
        BOOST_ASSERT_MSG可以在出错的时候附加一段用户自定义的错误描述信息,帮助用户更好的理解及处理错误。
        如果不定义BOOST_ENABLE_ASSERT_HANDLER,那么BOOST_ASSERT_MSG会将断言相关的信息转发到BOOST_ASSERT_MSG_OSTREAM,默认是std::cerr,并且接下来调用std::abort()。如果需要自定义输出流,用户需要在包含<boost/assert.hpp>前指定BOOST_ASSERT_MSG_OSTREAM的值。
        BOOST_VERIFY的功能和BOOST_ASSERT基本一致,所不同的是BOOST_VERIFY的表达式总是被求值,即通过NDEBUG和BOOST_DISABLE_ASSERTS禁用断言时,表达式依然会被求值,但是结果会被丢弃。
注意:
        <boost/assert.hpp>没有头文件guard,这是为了保证多次包含此文件时,通过预先定义不同的宏,实现不同的断言。
源码剖析:

//
//  boost/assert.hpp - BOOST_ASSERT(expr)
//                     BOOST_ASSERT_MSG(expr, msg)
//                     BOOST_VERIFY(expr)
//
//  Copyright (c) 2001, 2002 Peter Dimov and Multi Media Ltd.
//  Copyright (c) 2007 Peter Dimov
//  Copyright (c) Beman Dawes 2011
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
//  Note: There are no include guards. This is intentional.
//
//  See http://www.boost.org/libs/utility/assert.html for documentation.
//

//
// Stop inspect complaining about use of 'assert':
//
// boostinspect:naassert_macro
//

// Comment By:  凝霜
// E-mail:      mdl2009@vip.qq.com
// Blog:        http://blog.csdn.net/mdl13412  

//--------------------------------------------------------------------------------------//
//                                     BOOST_ASSERT                                     //
//--------------------------------------------------------------------------------------//

//  由于没有头文件guard,每次包含文件时都要undef掉宏定义,这样可以根据预定义的宏,实现不同版本的断言。
#undef BOOST_ASSERT

// 定义BOOST_DISABLE_ASSERTS,关闭断言,不会影响到标准库的assert
#if defined(BOOST_DISABLE_ASSERTS)

// 编译器面对这样的一个定义,一定会优化掉,如果你的编译器不能优化。。。相信它也不能正确编译boost库^_^
# define BOOST_ASSERT(expr) ((void)0)

// 预定义BOOST_ENABLE_ASSERT_HANDLER的同时,需要用户自己实现assertion_failed,完成错误断言的回调。
#elif defined(BOOST_ENABLE_ASSERT_HANDLER)

#include <boost/current_function.hpp>

namespace boost
{
  void assertion_failed(char const * expr,
                        char const * function, char const * file, long line); // user defined
} // namespace boost

// 经典的assert实现,很简单,正确时不做任何事情,错误时调用用户自定义的回调函数,进行错误处理。
// BOOST_CURRENT_FUNCTION用于获取当前函数名称,需要编译器的支持,在<boost/current_function.hpp>中有实现。
#define BOOST_ASSERT(expr) ((expr) \
  ? ((void)0) \
  : ::boost::assertion_failed(#expr, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__))

  // 默认情况下,使用的是标准库中的assert,可以通过NDEBUG和BOOST_DISABLE_ASSERTS进行禁用。
#else
# include <assert.h> // .h to support old libraries w/o <cassert> - effect is the same
# define BOOST_ASSERT(expr) assert(expr)
#endif

//--------------------------------------------------------------------------------------//
//                                   BOOST_ASSERT_MSG                                   //
//--------------------------------------------------------------------------------------//

// 和 BOOST_ASSERT相比, BOOST_ASSERT_MSG可以附加一段用户自定义的消息,
// 这有助于描述断言信息,更快、更好的处理断言,定位错误。
# undef BOOST_ASSERT_MSG

#if defined(BOOST_DISABLE_ASSERTS) || defined(NDEBUG)

  #define BOOST_ASSERT_MSG(expr, msg) ((void)0)

#elif defined(BOOST_ENABLE_ASSERT_HANDLER)

  #include <boost/current_function.hpp>

  namespace boost
  {
    void assertion_failed_msg(char const * expr, char const * msg,
                              char const * function, char const * file, long line); // user defined
  } // namespace boost

  #define BOOST_ASSERT_MSG(expr, msg) ((expr) \
    ? ((void)0) \
    : ::boost::assertion_failed_msg(#expr, msg, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__))

#else
// 注意:这里需要头文件guard,因为默认的行为只能被定义一次,否则会出现重复定义的问题。
  #ifndef BOOST_ASSERT_HPP
    #define BOOST_ASSERT_HPP
    #include <cstdlib>
    #include <iostream>
    #include <boost/current_function.hpp>

    //  IDE's like Visual Studio perform better if output goes to std::cout or
    //  some other stream, so allow user to configure output stream:
    // 用户可以通过预定义,将错误信息输出到不同的流,这个流甚至可以是socket,只要你实现了ostream接口的封装。
    #ifndef BOOST_ASSERT_MSG_OSTREAM
    # define BOOST_ASSERT_MSG_OSTREAM std::cerr
    #endif

    namespace boost
    { 
      namespace assertion 
      { 
        namespace detail
        {
          // 打印断言错误信息,并且crash掉程序。
          // 发生异常的时候,最好的办法就是crash掉程序,如果忽略,会导致更多的错误。
          inline void assertion_failed_msg(char const * expr, char const * msg, char const * function,
            char const * file, long line)
          {
            BOOST_ASSERT_MSG_OSTREAM
              << "***** Internal Program Error - assertion (" << expr << ") failed in "
              << function << ":\n"
              << file << '(' << line << "): " << msg << std::endl;
            std::abort();
          }
        } // detail
      } // assertion
    } // detail
  #endif

  #define BOOST_ASSERT_MSG(expr, msg) ((expr) \
    ? ((void)0) \
    : ::boost::assertion::detail::assertion_failed_msg(#expr, msg, \
          BOOST_CURRENT_FUNCTION, __FILE__, __LINE__))
#endif

//--------------------------------------------------------------------------------------//
//                                     BOOST_VERIFY                                     //
//--------------------------------------------------------------------------------------//

#undef BOOST_VERIFY

#if defined(BOOST_DISABLE_ASSERTS) || ( !defined(BOOST_ENABLE_ASSERT_HANDLER) && defined(NDEBUG) )

// 在任何情况下,expr一定会被求值。
# define BOOST_VERIFY(expr) ((void)(expr))

#else

# define BOOST_VERIFY(expr) BOOST_ASSERT(expr)

#endif
实例:

我们在类里面定义一个断言,并让其为假。

#include <iostream>
#include <cstdlib>

#include <boost/assert.hpp>

using namespace std;

class Dummy
{
public:
    void foo()
    {
        const bool expr = false;
        
        BOOST_ASSERT(expr);
    }
};

int main(int argc, char** argv)
{
    Dummy dummy;
    dummy.foo();

    return 0;
}
boostsource: main.cpp:15: void Dummy::foo(): Assertion `expr' failed.

运行 失败 (退出值 1, 总计时间: 238毫秒)
我们看到,在断言失败后,错误被定位在main.cpp的第15行,其属于Dummy类的foo()函数。

接下来我们尝试使用BOOST_ASSERT_MSG

#include <iostream>
#include <cstdlib>

#include <boost/assert.hpp>

using namespace std;

class Dummy
{
public:
    void foo()
    {
        const bool expr = false;
        
        BOOST_ASSERT_MSG(expr, "Oops, XXX is invalid");
    }
};

int main(int argc, char** argv)
{
    Dummy dummy;
    dummy.foo();

    return 0;
}
***** Internal Program Error - assertion (expr) failed in void Dummy::foo():
main.cpp(15): Oops, XXX is invalid

运行 失败 (退出值 1, 总计时间: 355毫秒)
这次我们看到断言为假时,系统将我们定义的错误信息显示了出来。如果我们在断言的自定义消息部分给出了有意义的信息,那么在断言为假的时候,我们可以快速找出程序的Bug,显著提升调试的效率。
接下来我们设置自定义的回调函数,用于处理断言为假的情况。

#include <iostream>
#include <cstdlib>

#define BOOST_ENABLE_ASSERT_HANDLER
#include <boost/assert.hpp>

using namespace std;

namespace boost
{
void assertion_failed_msg(char const * expr, char const * msg,
                            char const * function, char const * file, long line)
{
    std::cout << "Something to handle assert" << std::endl;
}
}
  
class Dummy
{
public:
    void foo()
    {
        const bool expr = false;
        
        BOOST_ASSERT_MSG(expr, "Oops, XXX is invalid");
    }
};

int main(int argc, char** argv)
{
    Dummy dummy;
    dummy.foo();

    return 0;
}
Something to handle assert

运行 失败 (退出值 1, 总计时间: 343毫秒)
使用用户自定义的回调函数,我们拥有非常大的自由度,例如可以将错误信息写到日志,或者使用GUI将错误显示出来等等。

注意:用户自定义的回调函数只能定义一次!

最后再给出一个同时使用assert和BOOST_ASSERT_MSG的例子,这里你将看到我是如何通过HACK来让assert输出用户自定义的信息的:

#include <iostream>
#include <cstdlib>
#include <cassert>

// 禁用BOOST_ASSERT,不会影响std::assert
#define BOOST_DISABLE_ASSERTS
#include <boost/assert.hpp>

using namespace std;

class Dummy
{
public:
    void foo()
    {
        const bool expr = false;
        
        BOOST_ASSERT_MSG(expr, "Oops, XXX is invalid");
        assert(expr && "Haha... This is a hack");
    }
};

int main(int argc, char** argv)
{
    Dummy dummy;
    dummy.foo();

    return 0;
}
boostsource: main.cpp:19: void Dummy::foo(): Assertion `expr && "Haha... This is a hack"' failed.

运行 失败 (退出值 1, 总计时间: 224毫秒)
本例中,我将BOOST_ASSERT_MSG禁用,通过结果我们可以看出,这并没有影响到std::assert的功能。

另外输出中的

Assertion `expr && "Haha... This is a hack"
是我的HACK过程,可以模拟BOOST_ASSERT_MSG自定义消息的部分,在不是用Boost的时候,这个技巧非常有用。

总结:

Boost库的断言为我们提供了非常大的灵活性,与std::assert一起使用效果更佳。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值