问题描述
在 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} } 的构造函数:
- 一个将
my::thread
实体作为参数的函数,它允许对my::thread::properties
对象进行必要的操作。 - 要启动的函数。
也就是说,类似于
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