assert和static_assert及#error的使用

一、程序的错误控制

在实际的开发中,往往会遇到一些基础的控制,比如是否数组越界,或者类型不匹配等。一般可以使用if语句来控制,但在一些重要的场景上使用assert或者其它一些自定义的宏(verify等 )。随着C++标准的不断推进,在新标准里又提供了在编译期进行判断的关键字static_assert。
有人可能会说,可以用异常来处理控制这些问题,但实际上,C/C++原则上是不推荐使用异常来处理问题的,除了其本身语言无法提供更多的堆栈信息等外,更重要的是,在C++程序中,一般到了这种地步,程序要么直接崩溃了(内存异常);要么,程序再跑已经没有任何意义。可能在某些特定场合下,程序捕获异常是有意义的,这就需要开发者自己处理了。

二、运行期控制

在运行期进行错误控制一般使用assert,基本的示例如下:

#include <assert.h>
#include <iostream>
#include <string>

#define SHOW_KLEN = 1
constexpr int KLEN = 100;
int buf[KLEN] = {0};

void testAssert(int id) {
  assert(id < KLEN);
  std::cout << "id value:" << id << std::endl;
}

int main() {
  int index = 1000;
  testAssert(index);

  return 0;
}

但是使用assert的缺点在于,一旦出现问题,程序自动就挂了。然后会拿到一个assert的问题报告。如果此时用GDB去调试,发现崩溃的堆栈也在 assert上。所以其最好用于关键地方,一旦出问题,程序没有运行的必要了。

三、编译期控制

编译器控制有两种方式,一种是使用原来的#error,这种方式只会提供一个显示的错误标记。可以用在一些编译选项控制上,比如平台选择、OS选择等等。对于一些更精细的控制,可能就不太适合了。

#include <assert.h>
#include <iostream>
#include <string>

#define SHOW_KLEN = 1   //Code comments result in compilation errors

void testError(const std::string &s) {

#ifndef SHOW_KLEN
#error "do not insert db!"
#endif
  if (s == "insert") {
    //#error "do not insert db!"
  }

  std::cout << "operator is allow!" << std::endl;
}

int main() {
  std::string op = "insert";
  testError(op);

  return 0;
}

另外一种就是使用static_assert,它主要是在编译期控制值,保证按需要进行处理。它一般在模板编程上应用更广,特别是在元编程中,它可能会起到一些重要的作用。

#include <assert.h>
#include <iostream>
#include <string>

#define SHOW_KLEN = 1   //Code comments result in compilation errors
constexpr int KLEN = 100;

void staticassert() {
  static_assert(KLEN == 100);
  // static_assert(KLEN > 100);//build err
}

template <int N> struct IsPower2 {
  // Metaprogramming similar to (std:: is_integrial<T>:: value) can be used
  static_assert(N > 8, "must N > 8");
  static_assert((N & (N - 1)) == 0, "must power 2!");
};

template <class T> class Data {
public:
  void GetData(const T &t) { static_assert(sizeof(T) <= sizeof(short), "Data type size not short"); }
};

void testData() {
  Data<short> d;
  Data<decltype(KLEN)> d1;
  Data<std::decay_t<decltype(KLEN)>> d2;

  d.GetData(2);
  d1.GetData(KLEN);//build err
  d2.GetData(KLEN);//build err
}
constexpr int D = 16;//Non 2 power rule error
int main() {
  IsPower2<D>();
  testData();

  return 0;
}

其实写程序最重要的是学以致用,也就是常说的灵活运行知识。这三个基础的控制手段,特别是static_assert可以多看一下相关标准文档,再多看一下别人的代码,基本就能掌握,重点就是用。

四、总结

C/C++一直是外人认为的难学的语言,原因提过,就是太灵活。学得灵活,用得灵活,这让许多初学者很难一下子适应过来。其实最简单路就是最基础的方式,把基础打好。侯捷老师的原话:“勿在浮沙筑高台!”,非常贴切。尤其这几年看标准文档,越看越发现,这句话的重要性。配合着侯老师的另外一句话:“源码之前,了无秘密”。就明白了学好C/C++的最简单的方式了,打好基础(多看理论知识),多看代码(多多上机实践)。总结出自己一套学用C/C++的路来。