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

时间:2017-07-27 02:16:57

标签: python c++ multithreading macros 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;
}

我还尝试将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;
}

编辑:我知道第二个例子不做线程清理;然而,它编译。<​​/ p>

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

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

2 个答案:

答案 0 :(得分:4)

预处理器将宏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,因此您甚至不需要最后的其他部分。如果一切顺利,其他if语句只设置对SUCCESS的响应。

答案 1 :(得分:2)

来自[Python 3]: Py_BEGIN_ALLOW_THREADS重点是我的):

  

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

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

  • 预处理后,2 nd Py_END_ALLOW_THREADS 生成无效代码(#if defined (UBUNTU)中的内容无关紧要,因为当< em> UBUNTU 已定义,并且在未启用时将始终有效):

    • 引用(无意义)“ _save
    • 额外的右括号(“ } ”)

这两个宏的常见用例也在上一页中得到了示例:

PyThreadState *_save;
_save = PyEval_SaveThread();

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

PyEval_RestoreThread(_save);

为什么采用这种方式设计(包括范围界定)像您一样使用时可能会失败,因为那样可能会导致难以发现错误(您的示例非常简单,但是在更复杂的片段代码中,有许多分支需要的 Py_END_ALLOW_THREADS < / em>,想象一下错过一个或两次叫它意味着什么。

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

  • 不是直接返回失败,而是对其进行标记,最后返回(在一处),然后在返回之前将 Py_END_ALLOW_THREADS 放置
  • 在每个 if 分支( ARRAY PRIORITY_QUEUE ,...)中调用宏对(
  • 使用(可怕的) goto