如果未检查函数返回值,如何在 C++(17) 中强制编译错误?理想情况下通过类型系统 [英] How to force a compile error in C++(17) if a function return value isn't checked? Ideally through the type system

查看:37
本文介绍了如果未检查函数返回值,如何在 C++(17) 中强制编译错误?理想情况下通过类型系统的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在编写安全关键代码,我想要一种比 [[nodiscard]] 确保检查函数返回值被编译器捕获.

We are writing safety-critical code and I'd like a stronger way than [[nodiscard]] to ensure that checking of function return values is caught by the compiler.

[更新]

感谢评论中的所有讨论.让我澄清一下,这个问题可能看起来是人为的,或者不是典型用例",或者不是其他人会怎么做.如果这样更容易忽略你为什么不这样做呢?",请将此作为学术练习.问题正是如果没有将其分配给左值作为函数调用的返回结果,是否有可能创建一个编译失败的类型.
我知道 [[nodiscard]]、warnings-as-errors 和异常,这个问题询问是否有可能实现类似的东西,即编译时间 错误,不是在运行时捕获的东西.我开始怀疑这是不可能的,因此非常感谢解释为什么.

Thanks for all the discussion in the comments. Let me clarify that this question may seem contrived, or not "typical use case", or not how someone else would do it. Please take this as an academic exercise if that makes it easier to ignore "well why don't you just do it this way?". The question is exactly whether it's possible to create a type(s) that fails compiling if it is not assigned to an l-value as the return result of a function call .
I know about [[nodiscard]], warnings-as-errors, and exceptions, and this question asks if it's possible to achieve something similar, that is a compile time error, not something caught at run-time. I'm beginning to suspect it's not possible, and so any explanation why is very much appreciated.

约束:

  • MSVC++ 2019
  • 依赖警告的东西
  • Warnings-as-Errors 也不起作用
  • 持续运行静态分析是不可行的
  • 宏没问题
  • 不是运行时检查,而是被编译器捕获
  • 不是基于异常

我一直在想如何创建一个类型,如果它没有从函数返回值分配给一个变量,编译器就会标记一个错误.

I've been trying to think how to create a type(s) that, if it's not assigned to a variable from a function return, the compiler flags an error.

示例:

struct MustCheck
{
  bool success;
  ...???... 
};

MustCheck DoSomething( args )
{
  ...
  return MustCheck{true};
}

int main(void) {
  MustCheck res = DoSomething(blah);
  if( !res.success ) { exit(-1); }

  DoSomething( bloop ); // <------- compiler error
}
  

如果通过类型系统可以证明这样的事情是不可能的,我也会接受这个答案;)

If such a thing is provably impossible through the type system, I'll also accept that answer ;)

推荐答案

(EDIT) 注 1:我一直在思考你的问题,得出的结论是这个问题是不恰当的.由于一个小细节,不清楚您在寻找什么:什么算作检查?检查是如何组成的,离调用点还有多远?

(EDIT) Note 1: I have been thinking about your problem and reached the conclusion that the question is ill posed. It is not clear what you are looking for because of a small detail: what counts as checking? How the checkings compose and how far from the point of calling?

例如,这算作检查吗?请注意,布尔值(结果)和/或其他运行时变量的组合很重要.

For example, does this count as checking? note that composition of boolean values (results) and/or other runtime variable matters.

bool b = true; // for example
auto res1 = DoSomething1(blah);
auto res2 = DoSomething2(blah);

if((res1 and res2) or b){...handle error...};

与其他运行时变量的组合使得无法在编译时做出任何保证以及与其他结果"的组合.您必须排除某些逻辑运算符,例如 OR 或 XOR.

The composition with other runtime variables makes it impossible to make any guarantee at compile-time and for composition with other "results" you will have to exclude certain logical operators, like OR or XOR.

(EDIT) 注 2:我之前应该问过,但 1) 处理是否应该总是中止:为什么不直接从 DoSomething 函数中止?2) 如果处理在失败时执行特定操作,则将其作为 lambda 传递给 DoSomething(毕竟您正在控制它返回的内容以及需要的内容).3)故障或传播的组合是唯一不平凡的情况,并且在您的问题中没有明确定义.

(EDIT) Note 2: I should have asked before but 1) if the handling is supposed to always abort: why not abort from the DoSomething function directly? 2) if handling does a specific action on failure, then pass it as a lambda to DoSomething (after all you are controlling what it returns, and what it takese). 3) composition of failures or propagation is the only not trivial case, and it is not well defined in your question.

以下是原始答案.

这并不能满足您的所有(编辑过的)要求(我认为它们是多余的),但我认为这确实是唯一的前进道路.在我的评论下方.

This doesn't fulfill all the (edited) requirements you have (I think they are excessive) but I think this is the only path forward really. Below my comments.

正如您所暗示的,为了在运行时执行此操作,网上有关于爆炸"的食谱.类型(如果它们未被检查,则它们在销毁时断言/中止,由内部标志跟踪).请注意,这不使用异常(但它是运行时并且如果您经常测试代码,这并不是那么糟糕,毕竟这是一个逻辑错误).

As you hinted, for doing this at runtime there are recipes online about "exploding" types (they assert/abort on destruction if they where not checked, tracked by an internal flag). Note that this doesn't use exceptions (but it is runtime and it is not that bad if you test the code often, it is after all a logical error).

对于编译时,它更棘手,用 [[nodiscard]] 返回(例如一个 bool)是不够的,因为有不丢弃的方法不检查例如分配给 (bool) 变量.

For compile-time, it is more tricky, returning (for example a bool) with [[nodiscard]] is not enough because there are ways of no discarding without checking for example assigning to a (bool) variable.

我认为下一层是激活 -Wunused-variable -Wunused-expression -Wunused-parameter(并将其视为错误 -Werror=...).那么不检查 bool 就更难了,因为比较几乎只与您真正可以使用 bool 进行的操作相比.(您可以分配给另一个布尔值,但随后您将不得不使用该变量).

I think the next layer is to active -Wunused-variable -Wunused-expression -Wunused-parameter (and treat it like an error -Werror=...). Then it is much harder to not check the bool because comparison is pretty much to only operation you can really do with a bool. (You can assign to another bool but then you will have to use that variable).

我想这已经足够了.

仍然有马基雅维利的方式来标记一个已使用的变量.为此,您可以发明一个 bool 类似类型(类),即 1) [[nodiscard]] 本身(类可以标记为 nodiscard), 2) 唯一支持的操作是 ==(bool)!=(bool) (甚至可能无法复制)并从您的函数中返回它.(作为奖励,您不需要将您的函数标记为 [[nodiscard]] 因为它是自动的.)

There are still Machiavelian ways to mark a variable as used. For that you can invent a bool-like type (class) that is 1) [[nodiscard]] itself (classes can be marked nodiscard), 2) the only supported operation is ==(bool) or !=(bool) (maybe not even copyable) and return that from your function. (as a bonus you don't need to mark your function as [[nodiscard]] because it is automatic.)

我猜想避免像 (void)b; 这样的东西是不可能的,但它本身就成了一个标志.即使无法避免没有检查,您也可以强制使用至少会立即引起注意的图案.

I guess it is impossible to avoid something like (void)b; but that in itself becomes a flag. Even if you cannot avoid the absence of checking, you can force patterns that will immediately raise eyebrows at least.

您甚至可以结合运行时和编译时策略.(使 CheckedBool 爆炸.)这将涵盖很多情况,此时您必须感到高兴.如果编译器标志不能保护你,你仍然有一个可以在单元测试中检测到的备份(不管采取错误路径!).(现在不要告诉我你不对关键代码进行单元测试.)

You can even combine the runtime and compile time strategy. (Make CheckedBool exploding.) This will cover so many cases that you have to be happy at this point. If compiler flags don’t protect you, you will have still a backup that can be detected in unit tests (regardless of taking the error path!). (And don’t tell me now that you don’t unit test critical code.)

这篇关于如果未检查函数返回值,如何在 C++(17) 中强制编译错误?理想情况下通过类型系统的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆