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

err*_*ynn 5 c++ python macros multithreading c-preprocessor

我有一些代码应该是一个线程安全的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;
}
Run Code Online (Sandbox Code Playgroud)

我也尝试将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;
}
Run Code Online (Sandbox Code Playgroud)

编辑:我知道第二个例子不做线程清理; 然而,它编译.

Edit2: Py_BEGIN_ALLOW_THREADS扩展为{ PyThreadState *_save; _save = PyEval_SaveThread();

Py_END_ALLOW_THREADS扩展为PyEval_RestoreThread(_save); } 注意范围括号前置BEGIN和附加END.为什么宏观扩展的逻辑选择包括范围界定?

Cri*_*ati 2

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

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

所以,编译错误的答案很清楚:

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

    • 引用(不存在)“ _save
    • 额外的右大括号 (" } ")

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

PyThreadState *_save;
_save = PyEval_SaveThread();

... Do some blocking I/O operation ...

PyEval_RestoreThread(_save);
Run Code Online (Sandbox Code Playgroud)

为什么这样设计(包括范围界定)像您一样使用时可能会失败,因为这可能会导致很难发现错误(您的示例非常简单,但在更复杂的代码段中,有许多分支需要同样多的Py_END_ALLOW_THREADS,想象一下错过一个意味着什么,或者调用两次)。

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

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