在 C++ 中多次调用给定函数时,有没有办法引发编译时错误?

问题描述

在 C++ 中是否有一种方法可以设计一个函数/向它添加一些“属性”,以便在代码中多次调用它会引发编译时错误

提供一点背景/动机:我在 Mbed-OS 上编程,我犯了一些错误,如下所示:

rtos::Thread thread;

[lots of code]

thread.start(persistent_function_1);

[lots of code in a setup function]

thread.start(persistent_function_2);

这产生了(合乎逻辑的)结果,应该允许在程序的生命周期内执行的 persistent_function_1,直到线程被重新用于运行 persistent_function_2 .我花了很长时间才找到这个错误,我想知道我是否可以对我的 thread.start 函数做一些事情,以确保如果我再次犯这种错误,我会得到编译器错误

解决方法

我不认为有一种方法可以直接强制 C++ 语言在编译时检测 start() 的双重调用(换句话说,我认为 @user4581301 的建议行不通):静态断言您需要以某种方式更改实体的属性。我确定您可以使用 clang 编写自定义检查器,但我想这不是您想要的。显然,有可能有一个运行时断言报告一个已经start()ed 的线程再次启动。同样,这似乎不是您所追求的。

“显而易见”的解决方案是不要在函数中使用“[lots of code]”作为开始。事实上,std::thread 通过强制在对象声明和它的开始之间没有代码完全回避了这个问题:std::thread 在构造时开始。对象声明和开始之间带有“[lots of code]”的设置类似于

my::thread thread([&]{
        [lots of code]
        return persistent_function_1;
    }());

需要注意的是,您需要按顺序设置各种变量。也就是说,首选方法是在实际启动的站点声明 thread 对象:

[lots of code]
my::thread thread(persistent_function_1);

在这两种情况下,my::thread 都是围绕 rtos::thread 的简单包装,它不会公开单独的 start() 方法。由于我不知道为什么 rtos::thread 将构造和 start() 分开,一个合理的原因可能是能够设置各种线程参数,实际上使用两个单独的参数来 {{1} } 的构造函数:

  1. 一个将 my::thread 实体作为参数的函数,它允许对 my::thread::properties 对象进行必要的操作。
  2. 要启动的函数。

也就是说,类似于

thread

这样,仍然可以操作 my::thread thread([](my::thread::properties& properties) { [lots of code manipulating the properties] },persistent_function_1); ,但不能对一个线程进行两次 thread 操作。

,

一种选择是将线程包装在一个新的管理器对象中,大致形状为

class thread_manager {
  rtos::Thread thread;
  const std::function<...> execution_function;
  /* .
     .
     . */

public:
  thread_manager(rtos::Thread _thread,std::function<...> function,...)
    : thread { _thread },execution_function { function },...

  void start();
}

并禁止线程的任何其他用途(这可以在封装的基础上证明是合理的,尽管正如评论中指出的那样,雅虎总是存在风险)。

,

目前没有检测出现两次的表达式的机制。但是你可以折磨编译器来接近一些东西

namespace
{
    template<int>
    struct once
    {
        once() {}
        friend void redefine() {}
    };
}

#define ONCE(expr) (once<__COUNTER__>{},(expr))

如果 ONCE 在同一个 TU 中出现两次,编译器会抱怨重新定义 redefine

ONCE(thread.start(persistent_function_1));  // ok
ONCE(thread.start(persistent_function_2));  // error