首页 > 程序人生 > 把错误扼杀在编译期——static assert

把错误扼杀在编译期——static assert

随着泛型编程在C++中用得越来越广泛的使用,更好的静态检查(static checking)以及可定制型错误消息的需求显得越来越有必要。本文的主要内容是讲一些如何在编译期对程序做一些检查、把错误尽可能扼杀在编译期(Complie Time)的方法,这里称之为static assert技术。

Static assert技术主要是利用C++编译器及语言本身的特性来完成,它所用到的主要思想包括以下两点:

  • 1. 表达式在编译期计算所得的结果是常数
  • 2. 传给编译器一个表达式,如果是非零便合法,如果是零则非法
  • 有了上面的思想,我们便可以想办法,利用C++语言的特性,来实现static assert,这里不再卖关子,直接给出几点事实:

  • 1. 大小为零的数组(array)是非法的
  • 2. 用不完整类型(Complete Type)来定义变量,也是非法的
  • 3. sizeof(type)的表达式中,如果type是不完整类型,也是非法的
  • 以上三个特性都是编译器在编译期(Compile Time)检查的,因此,我们可以利用它们来实现static assert。下面我们来看看具体的实现:

  • 1. 用非零数组来实现:
  • 1
    
    #define STATIC_ASSERT(expr)   { char array[(expr) ? 1 : 0]; }
  • 2. 用不完整类型来实现:
  • 1
    2
    3
    
    template< bool expr> struct CompileTimeChecker;
    template<> struct CompileTimeChecker< true> {};
    #define STATIC_ASSERT(expr) {  CompileTimeChecker< (expr) != 0>(); }
  • 3. 用sizeof实现
  • 1
    2
    3
    
    template< bool expr> struct CompileTimeChecker;
    template<> struct CompileTimeChecker< true> {};
    #define STATIC_ASSERT(expr) {  (void)sizeof(CompileTimeChecker< (expr) != 0>); }

    以上便是static assert的实现,用户在写程序的时候,直接通过宏STATIC_ASSERT来检查给定的表达式,上面三种方法都达到编译器静态检查的目的。不过我个人比较推荐第三种方法,原因如下:

  • 1. 第一种和第二种方法定义了一个临时变量,相对第三种方法有额外的开销
  • 2. 第三种方法可以灵活地增加参数,更加友好地提示错误信息,下面就是一种可以提示错误信息的实现:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    template< bool expr> struct CompileTimeChecker
    {
        CompileTimeChecker(...);
    };
    template<> struct CompileTimeChecker< false> {};
    #define STATIC_ASSERT(expr, msg) \
        { \
    	    class ERROR_##msg {}; \
                (void)sizeof(CompileTimeChecker< (expr) != 0>(ERROR_##msg())); \
          }
    1. 2011年5月9日13:25 | #1

      小武哥,有个问题,这个是怎么用在实际代码里面的?
      是插入编译后再注释掉?还是仅放在unit test里面?

      btw:你可以用下SyntaxHighlighter Evolved,不会出现的问题

    2. 2011年5月9日13:47 | #2

      直接通过宏STATIC_ASSERT()调用就可以了啊,括号里放上你要做静态检查的表达式,如果表达式有问题,在编译的时候就会报错。
      (关于插件,我试下你说的,谢谢!)
      @Leon

    3. 2011年5月9日13:55 | #3

      这个对程序基本上没有额外的影响,因此可以直接用在程序中。就像c里面的assert一样的用,但这个是在编译期检查的,c里面的assert是在运行期检查的。@Leon

    1. 本文目前尚无任何 trackbacks 和 pingbacks.
    您必须在 登录 后才能发布评论.