C++ if 语句范围内的宏未编译

C++ macro in scope of if statement not compiling

本文关键字:编译 范围内 if 语句 C++      更新时间:2023-10-16

我有一些代码应该是线程安全的python/c ++ api。我正在使用宏Py_BEGIN_ALLOW_THREADSPy_END_ALLOW_THREADS,它们扩展以创建保存线程状态并创建锁。我在方法退出之前释放锁;一次在语句作用域内if一次,一次在方法作用域内。

为什么这不编译?它会生成错误:error: _save was not declared in this scope第二个Py_END_ALLOW_THREADS宏。

uint8_t SerialBuffer::push_msg() {
#if defined (UBUNTU)
Py_BEGIN_ALLOW_THREADS
#endif
if (_type == ARRAY) {
// array access
} else if (_type == PRIORITY_QUEUE) {
// queue access
} else {
// Placing the return statement in the preprocessor directive
// has no effect.
#if defined (UBUNTU)
Py_END_ALLOW_THREADS
#endif
return FAIL;
}
#if defined (UBUNTU)
Py_END_ALLOW_THREADS
#endif
return SUCCESS;
}

我还尝试将return语句放在#if指令范围内,这会产生相同的错误。但是,这有效:

uint8_t SerialBuffer::push_msg() {
#if defined (UBUNTU)
Py_BEGIN_ALLOW_THREADS
#endif
if (_type == ARRAY) {
// array access
} else if (_type == PRIORITY_QUEUE) {
// queue access
} else {
// NOTE lack of #if directive here.
// Even though if this code executes the code below will not.
// Seems like a relatively simple problem for lambda calculus, no?
return FAIL;
}
#if defined (UBUNTU)
Py_END_ALLOW_THREADS
#endif
return SUCCESS;
}

编辑:我知道第二个示例不进行线程清理;但是,它会编译。

编辑2:Py_BEGIN_ALLOW_THREADS扩展到{ PyThreadState *_save; _save = PyEval_SaveThread();

Py_END_ALLOW_THREADS扩展到PyEval_RestoreThread(_save); }请注意,范围大括号在前面加上BEGIN,并附加END。为什么宏扩展包含范围是合乎逻辑的选择?

预处理器将宏Py_BEGIN_ALLOW_THREADS展开为创建名为_save的本地对象的代码。

预处理器将宏Py_END_ALLOW_THREADS扩展为使用_save执行线程清理任务的代码。

如果将Py_BEGIN_ALLOW_THREADS放在 else 块中,则Py_END_ALLOW_THREADS创建的代码看不到本地_save对象,因此会收到错误消息。

在相关主题上,我建议将Py_BEGIN_ALLOW_THREADSPy_END_ALLOW_THREADS放在如果第一个执行的位置,那么第二个执行。如果您有数组类型或优先级队列类型,则函数的第二个版本不会执行Py_END_ALLOW_THREADS线程清理任务。

试试这个:

uint8_t SerialBuffer::push_msg() {
#if defined (UBUNTU)
Py_BEGIN_ALLOW_THREADS
#endif
uint8_t response = FAIL;
if (_type == ARRAY) {
// array access
response = SUCCESS;
} else if (_type == PRIORITY_QUEUE) {
// queue access
response = SUCCESS;
}
#if defined (UBUNTU)
Py_END_ALLOW_THREADS
#endif
return response;
}

在此版本中,默认响应是 FAIL,因此您甚至不需要最后的 else 部分。另一个 if 语句仅在一切顺利时设置 SUCCESS 的响应。

来自 [Python.Docs]:初始化、终结和线程 - Py_BEGIN_ALLOW_THREADS(重点是我的(:

此宏扩展到{ PyThreadState *_save; _save = PyEval_SaveThread();.请注意,它包含一个左大括号;它必须与以下Py_END_ALLOW_THREADS宏匹配。有关此宏的进一步讨论,请参见上文。

因此,编译错误的答案非常明确:

  • 预处理后,第 2Py_END_ALLOW_THREADS生成无效代码(并且#if defined (UBUNTU)中的封闭无关紧要,因为它在定义UBUNTU时永远不会工作,而在未定义时它将始终有效(:

    • 引用(不存在(">_save"
    • 一个额外的右大括号 (">}"(

上页还举例说明了 2 个宏的常见用例:

PyThreadState *_save;
_save = PyEval_SaveThread();
... Do some blocking I/O operation ...
PyEval_RestoreThread(_save);

为什么以这种方式设计(包括范围(像你一样使用时可能会失败,因为这可能会导致难以找到错误(你的示例非常简单,但是在一个更复杂的代码中,许多分支需要同样多的Py_END_ALLOW_THREADS,想象一下错过一个意味着什么,或者调用它两次(。

为了解决您的问题,您必须重新设计您的代码,以便:

  • 不是在失败时直接返回,而是标记它,在最后返回(在一个地方(,并在返回之前放置Py_END_ALLOW_THREADS
  • 调用每个(或在需要时(if分支(ARRAY,PRIORITY_QUEUE...(中的宏对
  • 使用(可怕的(转到