-
Notifications
You must be signed in to change notification settings - Fork 85
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
Remove <iterator> header from span.hpp #179
Conversation
comment out iterator header include to see if it breaks something
In this case we can probably get by without that include because Incidentally, |
Ah, my mistake, should have checked this better, I noticed typedefs, but I presumed they are all just raw pointers/references.
As for
(libc++ uses some algo headers, but not the full |
FWIW, my principle is that whatever is directly used in the header must be included in that header. That is, I try to never rely on transitive includes. If including As for |
I'm not sure why we need the special case for |
I suspect that's because there there are slight differences between constructors from BTW, I think, the constructor from ranges when the span extent is not dynamic is poorly defined. Specifically, if the span extent is larger than the range size, the constructor will happily succeed and produce a span that is larger than the original range. I would rather such code just not compile at all. |
Ah, right, |
If you are referring to this scenario
|
Interesting. This doesn't make much sense to me, given the recent push towards increased safety. |
Yes, and I consider this a bug in the specification. And we should probably do better in Boost. I.e. either not offer this constructor (which is easy) or restrict it so that it is only enabled when it's safe (which may be more difficult). |
Ah, the above only compiles for |
Yes, but it doesn't make the constructor from a range safe. |
How could the constructor from a range be made safe? The range size isn't known until runtime. |
One idea is https://eel.is/c++draft/span.cons#15.1 should be made not precondition but a constraint. Which would mean Another idea is to perform a runtime size check and throw if the check fails. In any case, I think the support for static span size is a design misfeature that adds more problems than benefit. This is one of the reasons why I still prefer |
As for runtime check, the standard makes this a precondition, which means that it could be checked. This would at least mean a BOOST_ASSERT in our case, but it doesn't look like we have it. https://godbolt.org/z/xqhMWEYox |
Ok, let's paraphrase
Sorry, this reasoning doesn't work. We have at least one implementation that doesn't check, which means the code cannot rely on the check and therefore is not safe. Historically, preconditions were never enforced by implementations other than in special modes, like debug or even stronger "safe" modes that were not intended for production. IMO, the constructor in question is way too error-prone to rely on the user satisfying the precondition. |
BTW, the case of |
Yes, my mistake for not being explicit. I used I was curious if we could detect that size is fixed since it is available at compile time, but it turns out that since they made vector constexpr this will not work, e.g. this program compiles
It may be possible to make a "good" concept for detecting fixed size ranges so that
Yes, I picked |
A detail reverse_iterator in span is also an option. It would be unfortunate if the appreciation in libraries assuming C++11 or higher now is offset by them reimplementing even parts of C++03 for these reasons. |
Come to think of it, we might still need to include |
Yes, I managed to discover this twice (because I forgot). I occasionally need such a detection, but I haven't been able to find a way to do this reliably, so I settled on an explicit trait: https://github.com/pdimov/hash2/blob/develop/include/boost/hash2/has_constant_size.hpp |
Looks like this is going to change: cplusplus/papers#2125
So you're for the outright removal of this constructor? I myself have never been particularly fond of statically sized spans and the associated complexity they brought, but on the other hand, not matching the standard exactly almost always creates complaints. In either case, we are missing at least one BOOST_ASSERT here, aren't we, folks. |
Yes, shame, would be nice if C++ decades ago decided to use member presence to detect iterator category, e.g.
Also would be nice for boost if
In any case I think feasibility of getting rid of Let me elaborate:
In this case Sorry for the long comment that turned into policy suggestion, but I believe it would be nice if boost.core picked an approach(either 2. or 3.) or better one I did not consider. While this analysis may appear to support merging the pull request, actually I am fine with it being rejected and accepting that |
Any "let's make our headers more granular" standard proposal now gets answered with "modules fix everything". Let's hope modules do indeed fix everything, because standard headers are getting heavier and heavier with each subsequent standard. On our side, I always include the standard header the standard says needs to be included and seethe. There are some tricks you can legitimately play, such as including |
I think:
All of the above is regarding the fixed-size span. The dynamically sized span does not need any size checks, obviously. |
As far as I know real life results of Office experiment are a bit better(around 15%) than PGO. Not great, not terrible, but definately not just include everything you want and it is still 20x faster than headers.
To play defense attorney for static extent re matching standard: re asserts and runtime checks: |
If I understood discussion above correctly issue is that we can not detect types with compile time known size using SFINAE/concepts. We must explicitly list them or they must explicitly proclaim themselves to be of constant size. That may be one of reasons(beside explicit) why edit: with concepts it might work, but not sure how reliable it would be since concepts are syntax, not semantic checks. |
My hope is to live long enough to see modules ubiquitous. Because it doesn't seem like they will be of much help while half of the code base uses includes and the other half modules, yet this is what we can expect to see in the coming decades.
My approach in public headers is:
|
Maybe I should have made it more explicit that my suggestion of option 3. only applies to Boost.Core. As you know documentation says:
So even if |
The main rationale is separation of complexity and use cases. If you look at The interface complexity of span also leaks onto the users, as if you want to have And also I would say that fixed-sized spans are more rarely used and the use cases are different from the dynamic sized ones. Similarly to how they are different for |
Ah, right, I forgot about that. |
Not experienced enough to answer about generic algorithm, I presume I would just use |
Hi @pdimov @Lastique First part is investigation of what causes constexpr evaluation failures(beside obvious stuff like accessing wrong memory or calling non constexpr function).
Answer is that it depends on if we use libc++ or libstdc++ since libstdc++ has checks(and they are enabled in constexpr mode always). I think this makes sense and this old comment seems to suggest it is not an accident. Second step we need to do is to do a bit of poking of type from which span is constructed ( Third thing is to specify what we know about
If answers are no, no we learn nothing, as maybe it is just a container that is not constexpr friendly. But what is interesting is the following: if Full example is here. As mentioned downsides are that we are relying on check in But still does not seem like a terrible idea to do checks like this even if only for libstdc++. But to be honest seems like a lot of machinery to add to Boost.Core, so I am happy to hear if you have some suggestions to shrink it down. I have not measured compile times impact, godbolt link is super slow due to |
I'm not sure this will buy us much, because it's very uncommon for a fixed size The only example that comes to mind is |
I think this is too much of a stretch. Template parameters can have any meaning, we can't be guessing. I don't think this is something we can use as a criteria for detecting fixed-size ranges. Looks like the best we can do right now is to check static sizes for the ranges we know ( |
Well it also checks
this does not compile
Not always true if I read current code correctly. True for Beside other span and array implementations I think this might also catch some wrong usages of Don't get me wrong, I do not want In any case if you think this is an overkill I kind of agree, I just wish we could do proper checks without such a complex mechanism needed. At least I learned some new C++ tricks in past few days. :) |
We can forward-declare anything from Boost (e.g. boost::array), so we don't need to include any headers; only std:: is a problem. I think that I also tried |
Can't we just add a deleted overload that is enabled when |
And maybe another when |
Yes, but that will not work for all cases. As mentioned above that works for |
I would say not
Great suggestion, did not think of that since most of time it is some huge number(e.g. for |
This will be problematic if I suppose, for types that don't pass this check, for one reason or another, we would just do a runtime size check. So, the overload can be considered as just an optimization to eliminate the runtime overhead. |
True in my experiments, that is first thing I tried, then I felt silly because obviously you can not call methods on "invented" instance, you need to really construct the object. |
For types that don't pass the check, the overload will be dropped, and the "normal" one will be selected.
|
Something like this: https://godbolt.org/z/YGnj8GW1E |
Not sure that will help, as paper mentions static member function. Sure
This looks very nice! It does not solve the problem of
In my example I just say if result of |
No, the function doesn't have to be static. See https://godbolt.org/z/r7or63G8n. This only works with GCC 14. |
It does, yes. I suppose we need the |
If you want I can make the pull request. It will take a lot more time than if you did it, but I would be quite happy to do it. Will make sure to add tests, not sure how to test compile failures, probably there are some examples I can copy from. |
@glenfe is the maintainer of |
On a not entirely unrelated note,
the logic in Boost.Move that tries to play games to get |
Looks like now lives in https://github.com/llvm/llvm-project/commits/main/libcxx/include/__utility/swap.h |
See boostorg/move@b7a04d3 for a fix. Let me know if we should merge this into master. |
I think it's worth merging, provided that the tests pass. |
Regression tests ok: https://github.com/boostorg/move/actions/runs/12057774672/job/33623124544, asking for permission to merge to release managers. |
Hi @pdimov I am not really familiar with C++11 TMP, so I wonder if you know a better way to
|
@glenfe since you are maintainer of span: |
edit: this description missed use of
std::reverse_iterator
, please see comments bellowDue to reasons unrelated to this pull I was looking into how slow/fast to compile
span.hpp
and<span>
are.I have noticed that
<span>
compiles faster and that preprocessed output (-E
compiler flag) has around 20k lines less.It turns our that is mostly due to include of
<iterator>
header. This measurement is limited to just my setup(libstdc++) and results may be different for some other std implementation.As far as I know
<iterator>
is not needed for span functionality, and tests seem to pass. Note that I am not a boost dev so IDK how to properly test all users of this header, but for example I have tested beast beside running tests in core and all seems fine.This is not super important change since huge percentage of users will eventually use some part of STL that will drag in
<iterator>
header, but I still believe we should fix this(if nothing is broken by this change) for 2 reasons:<iterator>