I want to use Boost.Filesystem together with -fno-exceptions. According to the Boost.Filesystem documentation it states that it supports the BOOST_NO_EXCEPTIONS macro.
However, the following snippet:
#define BOOST_NO_EXCEPTIONS
#include <boost/filesystem.hpp>
int main() {}
compiled with:
g++ -fno-exceptions boost_test.cpp
gives the error:
/.../boost/filesystem/operations.hpp: In constructor
'boost::filesystem::filesystem_error::filesystem_error(const string&,
boost::system::error_code)':
/.../boost/filesystem/operations.hpp:84:16: error:
exception handling disabled, use -fexceptions to enable
catch (...) { m_imp_ptr.reset(); }
I compile using gcc 5 and boost version 1.57 on Mac OSX (also tested on similar ubuntu setups).
I am wondering whether my understanding of BOOST_NO_EXCEPTIONS is right in that it should cover the usage of -fno-exceptions or whether it's simply there for the boost::throw_exception part?
Well, "no" is the obvious answer here, g++ cannot deal with the filesystem_error class. There's a humdinger in boost/filesystem/config.hpp:
// throw an exception ----------------------------------------------------------------//
//
// Exceptions were originally thrown via boost::throw_exception().
// As throw_exception() became more complex, it caused user error reporting
// to be harder to interpret, since the exception reported became much more complex.
// The immediate fix was to throw directly, wrapped in a macro to make any later change
// easier.
#define BOOST_FILESYSTEM_THROW(EX) throw EX
This macro is used extensively in libs/filesystem/src/operations.cpp to throw exceptions. This is a show-stopper.
Fwiw, your sample program only appears to compile properly in clang and MSVC++, they only complain in their back-end about having to emit exception handling code, g++ does it in its front-end. No complaint from clang/msvc++ for this sample code since that exception handling code was already emitted previously, back when the boost libraries were built.
Which demonstrates another severe problem with your approach, you probably originally built boost without -fno-exceptions in effect. Not good.
This is what Boost.Filesystem the documentation says:
All exceptions thrown by the Filesystem Library are implemented by calling boost::throw_exception(). Thus exact behavior may differ depending on BOOST_NO_EXCEPTIONS at the time the filesystem source files are compiled.
In my understanding, it doesn't really say it support BOOST_NO_EXCEPTIONS
And when I did a egrep -r BOOST_NO_EXCEPTIONS under the filesystem directory, I found nothing
And after I read the source code, it supports my guessing. There are many places using try { ... } catch(...) in the code. You can also tell from the error message you got. Here is an example:
filesystem_error(
const std::string & what_arg, const path& path1_arg,
const path& path2_arg, system::error_code ec)
: system::system_error(ec, what_arg)
{
try
{
m_imp_ptr.reset(new m_imp);
m_imp_ptr->m_path1 = path1_arg;
m_imp_ptr->m_path2 = path2_arg;
}
catch (...) { m_imp_ptr.reset(); }
}
And if you read this, the semantic of BOOST_NO_EXCEPTIONS is actually not disabling exceptions, but:
forwarding all exceptions to a user-defined non-template version of
boost::throw_exception.
Related
When compiling following program with Xcode 10 GM:
#include <iostream>
#include <string>
#include <variant>
void hello(int) {
std::cout << "hello, int" << std::endl;
}
void hello(std::string const & msg) {
std::cout << "hello, " << msg << std::endl;
}
int main(int argc, const char * argv[]) {
// insert code here...
std::variant< int, std::string > var;
std::visit
(
[]( auto parameter )
{
hello( parameter );
},
var
);
return 0;
}
I get the following error:
main.cpp:27:5: Call to unavailable function 'visit': introduced in macOS 10.14
However, if I change min deployment target to macOS 10.14, the code compiles fine and it works, even though I am running macOS 10.13.
Since std::visit is function template, and should not depend on OS version (which I proved by running the code on lower version of mac than actually supported), should this be considered as bug and reported to Apple or is this expected behaviour?
The same happens when compiling for iOS (iOS 12 is minimally expected).
All std::variant functionality that might throw std::bad_variant_access is marked as available starting with macOS 10.14 (and corresponding iOS, tvOS and watchOS) in the standard header files. This is because the virtual std::bad_variant_access::what() method is not inline and thus defined in the libc++.dylib (provided by the OS).
There are several workarounds (all technically undefined behaviour), ordered by my personal preference:
1) Grab into the Implementation
std::visit only throws if one of the variant arguments is valueless_by_exception. Looking into the implementation gives you the clue to use the following workaround (assuming vs is a parameter pack of variants):
if (... && !vs.valueless_by_exception() ) {
std::__variant_detail::__visitation::__variant::__visit_value(visitor, vs...);
} else {
// error handling
}
Con: Might break with future libc++ versions. Ugly interface.
Pro: The compiler will probably yell at you when it breaks and the workaround can be easily adapted. You can write a wrapper against the ugly interface.
2) Suppress the Availability Compiler Error ...
Add _LIBCPP_DISABLE_AVAILABILITY to the project setting Preprocessor Macros ( GCC_PREPROCESSOR_DEFINITIONS)
Con: This will also suppress other availability guards (shared_mutex, bad_optional_access etc.).
2a) ... and just use it
It turns out that it already works in High Sierra, not only Mojave (I've tested down to 10.13.0).
In 10.12.6 and below you get the runtime error:
dyld: Symbol not found: __ZTISt18bad_variant_access
Referenced from: [...]/VariantAccess
Expected in: /usr/lib/libc++.1.dylib
in [...]/VariantAccess
Abort trap: 6
where the first line unmangles to _typeinfo for std::bad_variant_access. This means the dynamic linker (dyld) can't find the vtable pointing to the what() method mentioned in the introduction.
Con: Only works on certain OS versions, you only get to know at startup time if it does not work.
Pro: Maintains original interface.
2b) ... and provide your own exception implemention
Add the following lines one of your project source files:
// Strongly undefined behaviour (violates one definition rule)
const char* std::bad_variant_access::what() const noexcept {
return "bad_variant_access";
}
I've tested this for a standalone binary on 10.10.0, 10.12.6, 10.13.0, 10.14.1 and my example code works even when causing a std::bad_variant_access to be thrown, catching it by std::exception const& ex, and calling the virtual ex.what().
Con: My assumption is that this trick will break when using RTTI or exception handling across binary boundaries (e.g. different shared object libraries). But this is only an assumption and that's why I put this workaround last: I have no idea when it will break and what the symptoms will be.
Pro: Maintains original interface. Will probably work on all OS versions.
This happens because std::visit throws an bad_variant_access exception in cases described here and since the implementation of that exception depends on an newer version of libc++ you are required to use versions of iOS and macOS that ship this new version (macOS 10.14 and iOS 12).
Thankfuly, there is a implementation path available for when c++ exceptions are turned off which doesn't depend on the newer libc++ so if possible you can use that option.
P.S.
About the case where you increased the minimum deployment target to 10.14 and were still able to run the program normally on 10.13 I'm guessing you would run into problems at the point that this new exception would be triggered (since the exception method which relies on a newer version of libc++ would not be resolved).
Here's another alternative (that won't be palatable for some). If you're already using Boost, then you can use Boost.Variant2 when targeting iOS.
#if MACRO_TO_TEST_FOR_IOS_LT_11
#include <boost/variant2/variant.hpp>
namespace variant = boost::variant2;
#else
#include <variant>
namespace variant = std;
#endif
Then you can use variant::visit in your code.
I'm still working out the kinks to test for the iOS target version (and if we're targeting iOS at all). That's why I used MACRO_TO_TEST_FOR_IOS_LT_11 above, as a placeholder.
Similarly you can also use abseil-cpp libraries to seamlessly use std::variant where it is enabled, and abseil::variant where it isn't.
Abseil is an open-source collection of C++ code designed to augment the C++ standard library.
Add
CXXFLAGS += -D_LIBCPP_DISABLE_AVAILABILITY
to your Makefile.
See some of the other posts to see details on pros and cons of this, but this will get the code to compile and run.
Even though templates generally come from headers, doesn't mean the runtime target doesn't matter. Those templates are part of a wider library, and they compile to code that must still be compatible with the rest of that library. It makes sense for the whole standard library to be of one, single version, and it makes sense for that version to be the one that'll work on the target machine. Can you imagine the chaos that would ensue otherwise?
Some of the others here have given some low-level, practical reasons why in this particular case that version unity is important. Personally I think it's best to forget about implementation details like "templates go in headers" in situations like this; you shouldn't need to care about it, plus you risk making abstraction-breaking assumptions for little benefit. Just code to contract and you'll be fine.
I was running a MWE from here:
http://www.cplusplus.com/reference/ios/ios/exceptions/
On my machine it does not catch the exception. Here is my code
#include <iostream>
#include <fstream>
int main()
{
std::ifstream file;
file.exceptions( std::ifstream::failbit | std::ifstream::badbit );
try
{
file.open("IDoNotExist.txt");
}
catch(const std::ifstream::failure& e)
{
std::cout << "Bad luck!" << std::endl;
}
}
Using gcc 6.2.1 on Arch-Linux I get:
terminate called after throwing an instance of 'std::ios_base::failure'
what(): basic_ios::clear
However, on the link posted above it is mentioned that the code should also catch the exception related to opening the file. What went wrong?
It looks like a known bug in libstdc++.
The problem is that with the change to the C++11 ABI, many classes were duplicated in libstdc++6.so, one version with the old ABI, other with the new one.
Exception classes were not duplicated so this problem didn't exist at the time. But then, in some newer revision of the language, it was decided that std::ios_base::failure should derive from std::system_error instead of std::exception... but system_error is a C++11 only class so it must use the new ABI flag or it will complain. Now you have two different std::ios_base::failure classes and a mess in your hands!
The easy solution is to compile your program with -D_GLIBCXX_USE_CXX11_ABI=0 and resign to the old ABI until the bug is solved. Or alternatively, write catch (std::exception &e).
N.B. std::ifstream::failure is a type defined in the std::ios_base base class of ifstream, so the rest of this answer refers to it as std::ios_base::failure or just std::ios::failure.
The problem here is that since GCC 5 there are two different definitions of std::ios_base::failure in libstdc++ (see the Dual ABI docs for more details). That's needed because C++11 changed the definition of ios::failure from:
class ios_base::failure : public exception {
to:
class ios_base::failure : public system_error {
This is an ABI change, because system_error has additional data members compared to exception, and so it changes the size and layout of ios::failure.
So since GCC 5.1, when your code names std::ios_base::failure (or std::ifstream::failure or any other name for it) which definition you get depends on the value of the _GLIBCXX_USE_CXX11_ABI macro. And when an iostream error happens inside the libstdc++.so library, which type gets thrown depends on the value of the macro when libstdc++.so was built. If you try to catch one type and the library throws the other type, the catch won't work. This is what you're seeing. In your code std::ifstream::failure names the new type, but the library is throwing the old type, which is a different class, so the catch handler isn't matched.
With GCC 5.x and 6.x the code inside libstdc++.so throws the old type, so to catch it you need to either compile you code with -D_GLIBCXX_USE_CXX11_ABI=0 or change your handler to catch (const std::exception&) (because both the old and new types derive from std::exception).
With GCC 7.x the code inside the library throws the new type (changed by PR 66145), so you need to compile your code with -D_GLIBCXX_USE_CXX11_ABI=1 or catch std::exception.
For GCC 8.x the library now throws an exception type that can be caught by a handler for the old type or the new type (changed by PR 85222), so you don't need to change your code. It will Just Work™.
After experiencing crashes when introducing nested calls of std::async in my real program, I was able to reproduce the problem in the following minimum example. It crashes often, but not always. Do you see anything what goes wrong, or is it a compiler or standard library bug? Note that the problem remains if get() calls to the futures are added.
#include <future>
#include <vector>
int main (int, char *[])
{
std::vector<std::future<void>> v;
v.reserve(100);
for (int i = 0; i != 100; ++i)
{
v.emplace_back(std::async(std::launch::async, [] () {
std::async(std::launch::async, [] { });
}));
}
return 0;
}
I observe two different kinds of crashes: (in about every fifth run)
Termination with "This application has requested the Runtime to terminate it in an unusual way."
Termination after throwing an instance of 'std::future_error', what(): Promise already satisfied.
Environment:
Windows 7
gcc version 4.8.2 (i686-posix-dwarf-rev3, Built by
MinGW-W64 project), as provided by Qt 5.3.2
Command line call: g++ -std=c++11 -pthread futures.cpp
Compiled and run on two independent machines
Option -pthread?
Could it be that in my environment for some reason the option -pthread is silently not taken into account? I observe the same behavior with and without that option.
Since this answer is still "unanswered," after talking with some people from Lounge<C++>, I think I can say that it's pretty obvious from the comments that this is due to an implementation error either on MinGW/MinGW-w64's or pthread's part at the time. Using gcc 4.9.1, MinGW-W64, the problem does not appear anymore. In fact, the program above appears to compile and run correctly even on a version earlier than 4.8.2 with POSIX threading.
I myself am not an expert, my guess is that the exact trip-up happens when the program appears to try to write to the same promise twice, which, I think, should be a big no-no, as an std::async should write its result only once (again, I'm not sure if I'm right here, and other comments and edits will most likely clarify).
Also, this may be a related problem: std::future exception on gcc experimental implementation of C++0x
I know, that there are lot of similar questions at Stackoverflow. But none of answers can solve my problem.
I am creating DLL which exports some functions this way:
extern "C" __declspec(dllexport) int init() { ... }
I use MinGW 4.4 on Windows XP. There are exceptions in init(), because I use Apache Thrift, and there is code like ttransport->open(), where ttransport is boost::shared_ptr<TTransport>, and TTransport -- Apache Thrift's class. There are possible situations when ttransport->open() throws TTransportException exception (TTransportException inherited from std::exception).
That exception crashes host-program where my DLL is loaded. Host program was written by 3rd person and I have no code of host-program.
Thus, I'm wandering, why wrapper-approaches like this can't help (host program crashes the same way):
try
{
ttransport->open();
}
// or just catch(...)
catch (std::exception &e) // or, using TTransportException
{
return 42;
}
Can someone say what am I doing wrong?
Exceptions are not mine -- all was written in Apache Thrifts library, so I have no choice.
this can only be supported via SEH - most C/C++ compilers offer __try / __except / __finally for this... MINGW is NOT one of them...
There are some efforts to add support for SEH via macros and/or a library. These are not really production quality, it is more like "alpha code"... so I would refrain from using them without thorough testing (and most likely some modifications) in production...
Another point is that even if you get this to work on MINGW it might still cause problems with stack unwinding since for that you need compiler support which is not provided by MINGW for SEH - some details see here.
I'm using libjpeg right now to save JPEG images. If there is an error, libjpeg's default behavior is to call exit(), which I want to avoid since it's not a fatal error for my program. libjpeg allows you to use your own error manager, and mandates that if you use your own error_exit() function (which calls exit() by default) you must not return control to the caller. libjpeg suggests using setjmp.h to meet this requirement and not exit() the program.
However, I am writing a C++ program, and I have access to exceptions. This question's answer states it's safe (as in well-defined behavior) to throw an exception from the callback. But it doesn't mention dynamic libraries, and there's a general rule of thumb that you don't throw exceptions across dynamic library boundaries.
Here's an example:
#include <iostream>
#include <jpeglib.h>
#include <cstdio>
#include <stdexcept>
static void handleLibJpegFatalError(j_common_ptr cinfo)
{
(*cinfo->err->output_message)(cinfo);
throw std::runtime_error("error in libjpeg, check stderr");
}
int main()
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE* file = std::fopen("out.jpeg", "wb"); // assume this doesn't fail for this example
try
{
cinfo.err = jpeg_std_error(&jerr);
jerr.error_exit = handleLibJpegFatalError;
// let's say this triggers a fatal error in libjpeg and handleLibJpegFatalError() is called
// by libjpeg
jpeg_create_compress(&cinfo);
}
catch (...)
{
std::cerr << "Error saving the JPEG!\n";
}
jpeg_destroy_compress(&cinfo);
std::fclose(file);
}
What I would like to know is: can I throw an exception from this callback, and catch it back in my application, even if libjpeg is compiled as a dynamic library? libjpeg may be a static or dynamic library, and if it's a dynamic library it may possibly be built with a different compiler. However, the code that throws and catches the exception will certainly be in the same compilation unit. Is the above code safe?
FYI, I'm developing for OS X and Windows (and keeping the future of a Linux possibility in mind), so I'm more interested in if this is known to be well defined behavior in general, and not for a specific platform/compiler.
The other answer applies here. Nothing will get trashed when unwinding the stack. It doesn't even matter if the library uses some crazy calling convention internally, as long as it doesn't specifically mess with your C++ implementation's exception handling structures (which it wont as a C program). No C++ implementation that I know of finds the catch block by popping off stack frames (that would make optimization a nightmare), they all maintain internal structures for exception handling. As long as a call lower down in the call chain doesn't mess with those structures, stack unwinding will work perfectly fine for all of your personal code. Now, in general, it's quite possible that this would leave a library with a messed up internal state as you never return execution to the library for cleanup, but in the case of your error callback, libjpeg expects for control flow to not return and has presumably already cleaned up after itself.
In this case, I would go for it. In general, I would only throw fatal exceptions from a C callback.
Hope that helped.
It's not safe. Depending on how the relevant non-C++ library code was compiled, the necessary unwind tables may not exist. This is just a practical reason why it might fail; the conceptual reason is that it's simply undefined behavior.
You should follow the documentation and use setjmp/longjmp to get just outside the call to libjpeg code, then throw an exception immediately in the if (setjmp(...)) { ... } body if you want to use exceptions.
As discussed in other answers, it ought to be safe as long as you are in control of all the modules and they will be built by the same toolchain (inc. statically linking).
But I want to add a caveat here that some toolchains require this support to be turned on, since libjpeg's functions are marked extern "C". By default, Visual Studio assumes that such functions will not propagate exceptions.
If you don't turn this on, expect much pain. I spent hours on a testcase almost identical to yours before I realised this. 😃