On 01/14/18 03:27, Niall Douglas via Boost wrote:
, but having a virtual function call overhead whenever one wants to test if the error_code has a success value seems a too high price for that. I don't want to be paying that price in my Linux programs, sorry.
You have a completely misguided estimation of relative costs here. This argument has no basis in empirical reality.
Also, it's prejudiced as well. Your use case never sees more than one success value, so you fret about the "extra cost" of an indirect jump for testing that. But in my use case in AFIO I have to insert extra if statements to work around the fact that there are multiple success values possible from calls to the NT kernel. So I'm getting loaded with overhead so you don't have to be, and given the whole original design purpose of Boost.System which was to wrap C error code domains into better C++, I don't find that fair.
I assume that the extra `if` statements you're talking about are something like this: if (ec.category() == my_category() && (ec.value() < min_success || ec.value() > max_success)) { // handle failure } How would these tests be optimized if you moved the comparison into the category? The category test would be replaced with a virtual function call (which is arguably more expensive) but the error code value tests would still be executed.
(The reason I quote "extra cost" of an indirect jump is that there is no extra cost on any recent out of order CPU including ARM and Intel. It's literally zero cost in the hot cache case because it's been speculatively dereferenced, there isn't even a pipeline stall. Haswell and later can even make a virtual function call immediately calling a virtual function zero overhead in the hot cache case, the speculative execution reaches two levels down most of the time in most circumstances)
I don't think zero cost is a correct estimate. A virtual function call involves loading an entry from the virtual function table, which may stall and pollutes cache. Then there's the function body that consists of a few instructions that also need to be loaded and executed. Compare that to a couple (or four, in case of a range of codes to test for) inlined instructions which are likely already in the cache, prefetched with the rest of the function body. I'm sure the difference will be there, although it may not look that significant in absolute numbers. It will add up if you have multiple error code checks of have them in a loop.
It would still return a reference, just as now. You would check for internal nullptr, if so return a static null_category.
Why check in the first place if you could initialize the pointer to the global instance of the category?
It cannot currently be done portably on all current compilers.
It doesn't need to. `std::system_category` is implemented by compiler writers, they can use whatever magic they need. And they have already done that for iostreams.
But it cannot elide the potential call to an extern function, the function which retrieves the error_category&. And therefore must emit code, just in case the system_category might be fetched.
I don't think the current standard requires an external function call. The default constructor can obtain the pointer to the global category instance directly, if it is exported. `system_category()` can be an inline function as well.
You are incorrect. The C++ standard imposes a hard requirement that all error categories cannot - ever - have more than one instance in a process. That rules out inline sources of error categories.
It does not, if the category instance is exported from a shared library.
If you don't implement that, including in custom error categories, bad things happen, specifically error condition testing stops working right.
Custom error categories do not need any special treatment. Only the error category that is used in the default constructor of `error_code` (which is currently the system category) have to be available as an external object and initialized before the rest of the dynamic initialization. All other categories can continue to use function-local statics or whatever.
I don't have an estimate on how widespread custom error categories are, but I surely have a few. I know that several Boost libraries define their categories (namely, Boost.Filesystem, Boost.ASIO, Boost.Beast, Boost.Thread come to mind). Searching on GitHub[1] finds a few examples of custom categories (with a lot of noise from the comments though). Judging by that, I wouldn't say that custom categories are rare.
But they are however rare enough, and moreover, extremely easy to upgrade. My intended proposal for WG21 is that when you compile code with C++ 23 or later, the new string_view signature takes effect. If you compile with C++ 20 or earlier, the string signature remains.
Sounds like fun for implementers of the error categories that are supposed to be compatible with more than one language version. We already have the painful experience with virtual overrides in codecvt facets that changed at some point.
Some parts of your proposal (namely, switching to `string_view`) do not have a clear migration path, especially for C++03 users.
You didn't fully read my earlier email. There is no point using std::string_view with earlier than C++ 17. And boost::string_view doesn't have sufficient interop with std::string to substitute.
The std::string_view message() is 100% C++ 17 or later only.
My objection was not just about `std::string_view` being C++17-only, although it was part of it. Not all error categories can be ported to `std::string_view` in principle.