C++ C++;自定义断言

C++ C++;自定义断言,c++,c++11,expression,assert,C++,C++11,Expression,Assert,我正试图为自己的项目编写自己的自定义断言。这个项目将用c++11编写 断言必须具有以下特性: 它必须作为表达式保存,并且是可赋值的。 例如,我应该能够编写这样的代码intx=custom\u assert(1/y) 它必须重载才能接受带有消息的断言,而不接受没有消息的断言。 例如intx=custom_assert(1/y,“误差除以零”)此代码和以上代码都是可编译且可接受的 在释放模式下必须没有副作用 例如,intx=自定义断言(1/y)将变为intx=1/y处于释放模式 最重要的是,它必须在

我正试图为自己的项目编写自己的自定义断言。这个项目将用c++11编写

断言必须具有以下特性:

它必须作为表达式保存,并且是可赋值的。 例如,我应该能够编写这样的代码
intx=custom\u assert(1/y)

它必须重载才能接受带有消息的断言,而不接受没有消息的断言。 例如
intx=custom_assert(1/y,“误差除以零”)此代码和以上代码都是可编译且可接受的

在释放模式下必须没有副作用 例如,
intx=自定义断言(1/y)将变为
intx=1/y处于释放模式

最重要的是,它必须在断言产生的特定点中断。它将使用
\uu debugbreak()
作为其求值表达式的一部分

以下是我的尝试:

#include <string>

bool DoLog(std::string msg, std::string file, int line); //Prints to std::cerr and returns HALT macro


#if defined(_DEBUG) || defined(DEBUG)
#define HALT true
#define NT_ASSERT_BASE(x, msg) (!(x) && DoLog((msg), __FILE__, __LINE__) && (__debugbreak(),1))
#else
#define HALT false
#define NT_ASSERT_BASE(x,msg) (x)
#endif//debug/release defines

//--- Can't implement these until I can return the expression ---
//#define GET_MACRO(_1,_2,_3,NAME,...) NAME
//#define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2)(__VA_ARGS__)

#define NT_ASSERT(expression, msg) NT_ASSERT_BASE(expression,msg)
#包括
bool-DoLog(std::string-msg,std::string-file,int-line)//打印到std::cerr并返回HALT宏
#如果已定义(_DEBUG)| |已定义(DEBUG)
#定义HALT true
#定义NT_ASSERT_BASE(x,msg)(!(x)和&DoLog((msg),uuu FILE_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu行)和&(uuuuuu debugbreak(),1))
#否则
#定义暂停为false
#定义NT_断言_基(x,msg)(x)
#endif//debug/release定义
//---在返回表达式之前无法实现这些---
//#定义GET_宏(_1,_2,_3,名称,…)名称
//#定义FOO(…)GET_宏(uu-VA_-ARGS,FOO3,FOO2)(u-VA_-ARGS)
#定义NT\U断言(表达式,消息)NT\U断言\U基(表达式,消息)
正如您所看到的,我的自定义断言在两个方面失败,即保留为表达式和可赋值,以及重载(在我弄清楚如何将其保留为表达式之前,我无法实现重载)

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


非常感谢。

据我所知,这在标准C++中是做不到的

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

有两种选择:

  • 使用带有
    auto
    变量的gcc和clang的构造来保存结果。这将排除MSC++,但我想您需要这样做,因为
    \u debugbreak()
    是MSC++的错误特性

  • 放弃在调用站点上要求使用
    \u debugbreak()
    ,接受在停止时必须升级一级,并将其作为模板函数

    • lambda表达式比模板函数更适合。它将使中断出现在宏站点上,但仍将在调用堆栈中作为单独的堆栈框架出现。它还需要C++11支持(它在5年前发布,但某些平台可能没有)

我认为您不应该将验证与赋值混为一谈。从您的示例来看,您似乎想将其赋值给一个整数,但断言本质上是一个布尔表达式。此外,您的示例断言错误的表达式。您似乎想断言y不等于零(防止被零除),但您正在断言的东西也将是1、false或未定义的

如果您愿意灵活处理赋值要求,那么我们可以使用一些宏魔术来解决维护表达式和其他有用信息的问题。此外,我们可以在调用站点执行_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;
}
#包括
#包括
#包括
模板
内联bool DoLog(Fun f,std::string消息,常量字符*表达式,常量字符*文件名,int行){
静态断言(std::is_same::value,“谓词必须返回bool”);
如果(!(f()){

一个lambda不能这样做吗?
[](bool b,const char*msg){/*code*/}(param1,param2)
在我的工作中,我们使用了第二个建议,它工作正常;让调试器在检查表达式的模板函数中中断并没有那么糟糕;另一个技巧是在有消息时懒洋洋地对消息求值,这可以通过将消息填充到lambda中来实现——我见过断言从不命中,而是打印混乱的地方分析器中的年龄相当热门。@StoryTeller,是的,我忘记了这一点(我们仍然使用MSC++15.0,所以我不能在工作中使用它们)。lambda应该仍然显示为调用堆栈中的帧(至少在调试构建中),但它将是代码列表中的正确位置。@ChristopherLeong,既然您有C++11,您也可以尝试lambda,但它仍然会显示为一个额外的堆栈帧。为什么说
\u debugbreak()
是否不符合功能?是否限制使用c++11/14?项目允许使用c++11顺便说一句,在发布模式下保留的类似断言的表达式(减去实际断言)虽然这可能是微软的ism,但我相信它被称为。一般来说,我认为在发布版本中保留
assert
代码被认为是令人惊讶的。这需要一些澄清。给定
x=custom_assert(1/y);
,什么条件会使“assertion”失效?您的示例表明它被零除,但您的尝试表明它只是计算为0的表达式。为什么我不应该将验证与赋值混合使用?我假设验证是为了证明代码处理正确,并且可以轻松删除测试环境,而无需更改太多代码。我最大的担忧是n是多行程序的迭代用法,它只包含验证宏。例如:
bool isOpened=SomeSerializer->OpenFile(“somefile.txt”);assert(isOpened);if(isOpened){//code using the file}
相比之下,这似乎更优雅:
if(assert(SomeSerializer->Open(“somefile.txt”)){//code使用文件}
I