pybind / pybind11

Seamless operability between C++11 and Python
https://pybind11.readthedocs.io/
Other
15.55k stars 2.09k forks source link

[BUG]: Pybind11 conflicts with TBB because of the `PYBIND11_DEBUG_MARKER` hack #5136

Open adalisk-emikhaylov opened 4 months ago

adalisk-emikhaylov commented 4 months ago

Required prerequisites

What version (or hash if on master) of pybind11 are you using?

b07fddb21993d4fa358822dcbf2b36e0fd20db46

Problem description

Just tracked down a really cryptic bug. Pybind11's PYBIND11_DEBUG_MARKER hack (where it does #undef _DEBUG temporarily, and then defines it back) causes errors in TBB headers.

Here's what happens:

In other words, TBB by itself can handle any value of _DEBUG: both empty and 0/1. What it can't handle is the value suddenly changing from 1 to empty.

Proposed solution:

Improve the PYBIND11_DEBUG_MARKER hack to correctly reproduce the original value of the macro.

Here's how you store the value:

#define __PYBIND11_CONCAT_AUX(A,B) A##B
#define __PYBIND11_IS_MACRO_EMPTY(A,IGNORED) __PYBIND11_CONCAT_AUX(__PYBIND11_MACRO_EMPTY,A)
#define __PYBIND11_MACRO_EMPTY 1

#ifdef _DEBUG
#  if __PYBIND11_IS_MACRO_EMPTY(_DEBUG,IGNORED)==__PYBIND11_MACRO_EMPTY
#    define PYBIND11_DEBUG_MARKER_EMPTY
#  elif _DEBUG
#    define PYBIND11_DEBUG_MARKER_1
#  else
#    define PYBIND11_DEBUG_MARKER_0
#  endif
#  undef _DEBUG
#endif

And this is how you load it back:

#if defined(PYBIND11_DEBUG_MARKER_EMPTY)
#  define _DEBUG
#elif defined(PYBIND11_DEBUG_MARKER_1)
#  define _DEBUG 1
#elif defined(PYBIND11_DEBUG_MARKER_0)
#  define _DEBUG 0
#endif

The macro emptiness check is grabbed straight from TBB.

Reproducible example code

Compile with MSVC (or Clang-cl), and ensure _DEBUG is defined to 1.

#include <tbb/parallel_for.h>
#include <pybind11/pybind11.h>
#include <tbb/enumerable_thread_specific.h>

Is this a regression? Put the last known working version here if it is.

Not a regression