如果可能,静态断言,否则动态断言?

时间:2012-07-11 20:52:37

标签: c++ static-assert

假设我有一个模板函数,它接受一个整数和一个const引用类型为T的实例。现在,根据整数,只有一些T是可加的,否则在运行时抛出异常。

如果此函数的所有使用都使用常量整数,则可以使int成为模板参数并使用静态断言来检查它是否可接受。因此,不使用func(1,c),而是使用func<1>(c)并获得编译时类型检查。有没有办法编写func(1,c)并仍然保持编译时检查,同时还能够编写func(i,c)并使用动态断言?目标是使其对开发人员透明。添加这种安全性而不打扰开发人员关于编译时常量之类的东西会很棒。他们可能只记得func(1,c)始终有效并使用它,避免检查。

如何尽可能使用静态断言定义函数,否则动态断言?


以下代码显示了 Ivan Shcherbakov 的GCC解决方案:

#include <iostream>
#include <cassert>

template<typename T>
void __attribute__((always_inline)) func(const int& i, const T& t);

void compile_time_error_() __attribute__((__error__ ("assertion failed")));

template<>
  void __attribute__((always_inline))
  func(const int& i, const float& t)
{
    do {
        if (i != 0) {
            if (__builtin_constant_p(i)) compile_time_error_();
            std::cerr << "assertion xzy failed" << std::endl;
            exit(1);
        }
    } while (0);
    func_impl<float>(i,t);
}

这只允许i = 0和T = float的组合。对于其他组合,一个好方法是创建一个宏,用T生成代码template<> func(const int& i, const T& t),替换i!= 0。

2 个答案:

答案 0 :(得分:8)

好吧,如果你正在使用GCC,你可以使用脏黑客攻击,但只有在启用了函数内联(-O1或更多)时它才会起作用:

void my_error() __attribute__((__error__ ("Your message here")));

template <typename T1, typename T2> struct compare_types 
{
    enum {Equals = 0};
};

template <typename T1> struct compare_types<T1,T1> 
{
    enum {Equals = 1};
};

template <typename Type> __attribute__((always_inline)) void func(int a, Type &x)
{
    if (__builtin_constant_p(a))
    {
        if (a == 1 && compare_types<Type,char>::Equals)
            my_error();
    }
}

a == 1Typechar的情况下,您将收到错误消息。这是一个触发它的例子:

int main()
{
    char x;
    func(1, x);
    return 0;
}

请注意,此示例严重依赖于特定于gcc的__builtin_constant_p()函数,并且无法与其他编译器一起使用!

答案 1 :(得分:2)

让我在我的回答中解释问题更准确:

  

运行时可以断言有时报告编译时的错误。

Gcc可以,但仅限于某些优化级别和错误消息 非常没有信息。 Clang本身不能(没有错误属性),但确实如此 不要忘记铿锵分析器。 Analyzer可以报告一些运行时错误,例如解除引用空指针。

所以这是一个关于'智能'运行时断言的想法和简单测试:

#include <cstdlib> // std::abort

#if !defined(__clang__) && defined(__GNUC__)
// clang emulates gcc
# define GCC_COMPILER 1
#else
# define GCC_COMPILER 0
#endif

#if GCC_COMPILER
void assertion_failed_message() __attribute__((__error__("assertion failed")));
#endif

inline void smart_assert(bool condition) {
#if GCC_COMPILER
  // gcc is very 'sensitive', it must be first code lines in this function
  if (__builtin_constant_p(condition) && !condition) {
    assertion_failed_message();
  }
#endif

  if (condition) {
    // Ok
    return;
  }

#if defined(__clang_analyzer__)
  enum {
    ASSERTION_FAILED = 0xdeadfull
  };
  int *a = nullptr;
  *a = ASSERTION_FAILED;
#endif

  // Implement some standart error, like abort
  std::abort();
}

void test_condition_2(bool condition) {
  smart_assert(condition);
}

void test_condition_1(bool condition) {
  test_condition_2(condition);
}

void test_condition_0(bool condition) {
  test_condition_1(condition);
}

int main() {
  test_condition_0(0==1);
  return EXIT_SUCCESS;
}

G2在O2优化级别报告错误,这很好。但报告消息 是主要功能,不留任何信息 关于test_condition_ {0,1,2}。

Clang analyzer报告错误,如果您使用Xcode,则可以看到所有路径 从main到smart_assert: clang_analyzer_report

P.S。 clang分析器并不完美,所以如果你试试test_condition_0(argc), 不报告任何错误(真正的运行时检查),但如果你尝试 test_condition_0(argc == 1),将报告误报。