[Wheel-builders] manylinux wheel troubles using C++ and streaming exceptions

Vitaly Kruglikov vkruglikov at numenta.com
Sat Aug 6 05:43:25 EDT 2016


I added the ability to create a manylinux wheel for the nupic.core project (github.com/numenta/nupic.core). However, in testing, I found that some exception-handling logic that used to work is now failing when running the wheel on Ubuntu 16.04, which uses the gcc/g++ 5.4.0 toolchain. One example in particular is the failure to catch the exception std::ios::failure.

This makes it impractical to create manylinux wheels from legacy C++ code that also, as in the case of nupic.core, includes globs of additional 3rd party code. It's not practical because the incompatibilities may not be limited to just this one exception class and the actual failures are unreasonably difficult to simulate in testing, making it difficult, if not impossible, to find all occurrences in one's own code, not to mention 3rd party code that it links with.

It turns out that, among other things, "the C++11 standard mandated an ABI change for std::ios_base::failure, by giving it a std::system_error base class that wasn't present in C++03" (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66145). The 4.8.2 compiler toolchain on the manylinux docker image supports C++11 in spirit, but doesn't implement the new ABI (its std::ios_base::failure is derived from std::exception); however, the 5.4.0 toolchain on Ubuntu 16.04 implements the new ABI (derives from std::system_error, etc.), and its stdc++ library is compiled using the new toolchain. So, the signature of std::ios_base::failure compiled into the manylinux wheel doesn't match the signature of std::ios_base::failure that's raised by the stdc++ library on Ubuntu 16.04.

The following simple app demonstrates the issue:

```
#include <iostream>
#include <fstream>

int main() {

  try {
    std::ifstream input("notafile");
    input.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    input.get();
  } catch(const std::ios::failure &e) {
    std::cout << "caught a std::ios::failure: what=" << e.what()
      << '\n';
  }
}
```

First, we build and run it on the manylinux docker image:
$ docker run -it -v /home/vitaly/localbuilds/ios-base-failure:/ios-base-failure quay.io/pypa/manylinux1_x86_64 bash
[root at 39eecd65e630 ios-base-failure]# g++ -std=c++11 test.cpp

[root at 39eecd65e630 ios-base-failure]# ./a.out
caught a std::ios::failure: what=basic_ios::clear

As you can see above, running it on the manylinux container works correctly: it catches std::ios::failure and prints the expected message to stdout.

However, when we run the same executable on Ubuntu 16.04, we get a very different result below. We see that the exception wasn't caught.

g++ (Ubuntu 5.4.0-6ubuntu1~16.04.1) 5.4.0 20160609
('Ubuntu', '16.04', 'denial')

vitaly at ubuntuvm:~/localbuilds/ios-base-failure$ ./a.out
terminate called after throwing an instance of 'std::ios_base::failure[abi:cxx11]'
  what():  basic_ios::clear: iostream error
Aborted (core dumped)

Vitaly

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/wheel-builders/attachments/20160806/d0768d7e/attachment.html>


More information about the Wheel-builders mailing list