-
Notifications
You must be signed in to change notification settings - Fork 114
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bring back std exceptions (by default) #183
Comments
First of all thanks for the report and implementation issues. I'll open a separate bug for those implementation issues. I take those Reddit posts with a grain of salt, as they were thrown just after reading the changelog. Reddit commenters are It was not an easy change and I meditated quite a bit about it trying to find alternatives to avoid include bloat. No boost build broke with the change, all my private libraries compiled without problem (other than a missing include in some code that relied in indirect inclusion via Boost.Container, but I consider this a bug in that code) I certainly have projects that benefit for the change (simple classes that just need to use a container to store simple things), and users that already include will end including very few additional lines (about 100 additional lines, when including will bring thousands of lines) So the important question IMHO is how much code will break. I don't expect a lot of code will be broken, as experience shows very few people catch container exceptions (other than catching generic std::exception and calling "ex.what()"). I tried to minimize it deriving from std::exception and implemented the opt-out mechanism (I agree that the #define is not perfect but there is no other mechanism to avoid includes and other Boost libraries use #defines for features). Maybe a deprecation phase a release or two warning about the change would ease the transition, I take note on this. On the other hand, users have expressed they view Boost libraries as a "bloated". I certainly want to get rid of bloated includes for Boost.Container (dependencies on other Boost and std includes) and that will inevitably break some code. We have even compile/preprocess-time health surveys showing that Boost's vector is 3-4 times more "bloated" in lines of code (version 1.72) than standard vector (and it shouldn't): https://artificial-mind.net/projects/compile-health/ I'm not against reverting the change, I just would like to evaluate the real impact. If a Boost 1.76.1 bug-fix point release is done, I'm willing to make std exceptions default again and start a deprecation phase. I'll let your report open so that we can bring additional information on the issue while Boost.Container users test the library. |
Thanks for being open to discussion. Let me address some points:
I think people catch
I think the visibility is quite a big issue, although only on some systems. IIRC libstd++ uses the type name to catch types, but libc++ does not, so throwing one of the boost exceptions from a shared lib and catching it from outside will (likely) fail. But then again, only if people do catch specific exceptions and not the general std::exception
Who actually tests catching exceptions? ;-) And I did the "boost build"s include running tests or just compiling? If the latter I wouldn't expect problems, as syntactically it looks fine.
And I don't like it for exactly the same reason. C++ has this huge footgun with ODR(-violations), and I recently got hit by something like this and it is a real PITA to debug this as it depends on so many factors. With the current situation, unless you have full control over all defines in all dependencies, you don't know if a Container-exception or a std-exception will be thrown. Even when you set the define for your TUs, the linker might choose another. I really think a few potential ms of compile speed savings are not worth risking this.
From my understanding this is exactly for the opposite reason as what you did here: Boost includes a lot of other Boost stuff which often already (e.g the C++11 stuff) is in the standard. For example Boost using Boost.TypeTraits over Given that last thing makes me think, but in the end I'd take correctness and less code to write over compile time speed. Just my 5c, let's see what others may say and in the end it's of course your choice. |
Throwing your own |
These throw functions are much better placed in a compiled library (which would avoid the stdexcept include as a side effect), but this would probably be an unacceptable regression for the people who are currently using the library header-only. The current approach avoids including stdexcept, but emits the exception vtables in every translation unit, which creates more work for the compiler and the linker, so the end result is probably a wash. |
See for instance the difference between the generated code in https://godbolt.org/z/4ThETbnM8 and https://godbolt.org/z/sE8P5s13h. When |
It probably calls |
throw_bad_alloc is really used in the library when detecting overflow conditions in allocate functions, because the "memory unavailable" throw condition will be performed by operator new. I've reviewed that libstdc++ does the same but libc++ uses __throw_length_error and MSVC STL uses _Throw_bad_array_new_length. So maybe throw_bad_alloc uses could be replaced with something like throw_out_of_range, but I'm not sure what a users expects. Existing practice does not seem consistent |
My point was that if you're going to throw If we're talking about e.g.
|
I'd say
https://en.cppreference.com/w/cpp/container/vector/push_back |
"If any operation would cause in http://eel.is/c++draft/string.require#1, but isn't consistently used by the other containers, although it probably should be. |
Yes, I agree, I don't think I've expressed my point correctly. My point is that the library explicitly calls throw_bad_alloc only when overflow is detected. operator new, which is called in allocators to obtain memory, will throw std::bad_alloc. So maybe the library should get rid of the custom bad_alloc and throw other exception in the overflow detection case. So only std::bad_alloc would be thrown for out of memory conditions. |
d5a8304 replace the usage of std:: exceptions by new, custom ones which resulted in quite a few critics, e.g. on reddit: https://www.reddit.com/r/cpp/comments/mskpfp/boost_c_libraries_v1760
I have to agree and would suggest to revert that.
I think that is an example of optimization guided by microbenchmarks which usually fail in practice. I guess the size savings from the preprocessed lines come from avoiding std::string (transitively) but binary size will increase, as stdexcept will very likely be included somewhere (already or later). So my point is that this optimizations very likely pessimizes usual usecases.
Also there are implementation issues:
#ifdef BOOST_CONTAINER_USE_STD_EXCEPTIONS
checks in thethrow_*
functions-fvisibility=hidden
being the default (now) and no BOOST_SYMBOL_EXPORT or similar on the class<li>If BOOST_NO_EXCEPTIONS is NOT defined <code>std::logic_error(str)</code> is thrown.</li>
So IMO the easiest is be to revert that. Or at least don't make it the default so it is opt-in for users who want this optimization.
Note also that this may lead to ODR violations if one dependency defines it to use std exceptions and another does not. That is another reason to not do this.
The text was updated successfully, but these errors were encountered: