C ++自定义断言

时间:2016-01-26 15:58:50

标签: c++ c++11 expression assert

我正在尝试为自己的项目编写自己的自定义断言。该项目将使用c ++ 11编写。

断言必须具备以下特质:

必须将其保存为表达式并且可分配。 例如。我应该能够编写像int x = custom_assert(1/y);

这样的代码

必须重载才能接受带有消息且没有消息的断言。 E.g int x = custom_assert(1/y, "Error divide by zero");此代码及以上内容均可编辑且可接受。

在发布模式下必须没有副作用 例如。 <{1}}将在发布模式下成为int x = custom_assert(1/y);

最重要的是,它必须在断言所在的特定点处中断。这将使用int x = 1/y;作为其评估表达式的一部分。

以下是我的尝试:

__debugbreak()

正如你所看到的那样,我的自定义断言在2个方面失败,即保持为表达式和可分配,以及重载(在我弄清楚如何将其保留为表达式之前,我无法实现。

总而言之,我可能在追逐明星,而这个宏实际上可能无法实现。 (我希望不是这样的)

非常感谢。

2 个答案:

答案 0 :(得分:3)

据我所知,无法在标准C ++中完成

无法将__debugbreak()放入扩展代码中,同时传递未修改表达式的结果,因为您需要两次结果:一次用于测试它,它将隐式地将其转换为bool,最后一次返回。

有两种选择:

  • 使用gcc&c;和clang的({})构造与auto变量来保存结果。这将排除MSC ++,但我想你想要这样,因为__debugbreak()是MSC ++的错误。

  • 放弃在呼叫站点上要求__debugbreak(),接受在停止时必须升级一级并将该事物作为模板功能。

    • lambda表达式比模板函数略好。它将使中断显示在宏站点上,但它仍将在调用堆栈中显示为单独的堆栈帧。它还需要C ++ 11支持(它发布于5年前,但有些平台可能没有)。

答案 1 :(得分:2)

我认为你不应该将验证与作业混合在一起。从您的示例中,您看起来想要分配一个整数,但本质上,断言是一个布尔表达式。此外,您的示例是在错误的表达式上断言。看起来你想断言y不等于零(防止除零),但是你要断言也是一个或者是假的或者是未定义的。

如果您愿意对分配要求有一点灵活性,那么我们可以通过一些宏魔术来解决维护表达式和其他有用信息的问题。此外,我们可以在呼叫站点执行__debugbreak()。

#include <iostream>
#include <string>
#include <type_traits>

template<class Fun>
inline bool DoLog(Fun f, std::string message, const char *expression, const char *filename, int line) {
    static_assert(std::is_same<bool, decltype(f())>::value, "Predicate must return a bool.");
    if (!(f())) {
        std::cerr << filename << '@' << line << ": '" << expression << "' is false.";
        if (!message.empty()) {
            std::cerr << ' ' << message;
        }
        std::cerr << std::endl;
        return false;
    }
    return true;
}

#if defined(_DEBUG) || defined(DEBUG)
#define HALT true

#define WITH_MESSAGE_(expr, x) [&](){return (expr);}, x, #expr
#define WITHOUT_MESSAGE_(expr) [&](){return (expr);}, std::string{}, #expr
#define PICK_ASSERTION_ARGS_(_1, _2, WHICH_, ...) WHICH_
#define CREATE_ASSERTION_ARGS_(...) PICK_ASSERTION_ARGS_(__VA_ARGS__, WITH_MESSAGE_, WITHOUT_MESSAGE_)(__VA_ARGS__)
#define NT_ASSERT(...) if (!DoLog(CREATE_ASSERTION_ARGS_(__VA_ARGS__), __FILE__, __LINE__)) __debugbreak()
#else
#define HALT false
#define NT_ASSERT(...)
#endif

int main() {
    NT_ASSERT(true);
    NT_ASSERT(false);
    NT_ASSERT(1 == 1, "1 is 1");
    NT_ASSERT(1 == 0, "1 is not 0");
    return 0;
}

注意:上面的代码片段使用-std = c ++ 11(带有__debugbreak()语句的占位符)在GCC上工作。我假设VC ++在完全支持C ++ 11时也能正常工作。