Skip to content

Fix return type of templated span.subspan()#625

Merged
neilmacintosh merged 7 commits intomicrosoft:masterfrom
neilmacintosh:fix-subspan-return
Mar 4, 2018
Merged

Fix return type of templated span.subspan()#625
neilmacintosh merged 7 commits intomicrosoft:masterfrom
neilmacintosh:fix-subspan-return

Conversation

@neilmacintosh
Copy link
Contributor

This PR modifies the return type of the templated version of span::subspan() so that it returns a fixed-sized span wherever possible. This is important for encouraging optimization of code that uses subspan().

Writing the tests for this made me actually use span::extent which made me realize that it does not work unless you are using a C++17-compliant compiler, so I also added a workaround to make span::extent available on C++11/14 compilers until we can all live in the better world of C++17. ;-)

include/gsl/span Outdated
#if _MSC_VER < 1910
#pragma push_macro("constexpr")
#define constexpr /*constexpr*/
#define USE_STATIC_CONSTEXPR_WORKAROUND
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GSL_USE_STATIC_CONSTEXPR_WORKAROUND

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops! Thanks.


constexpr static const index_type extent = Extent;

#if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an odd choice. Why not simply provide the out-of-class definition for C++ < 17?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From memory: one of the compilers gave me a complaint about it. Plus, this seemed like the very well-known-but-ugly moral equivalent of the C++17 constexpr (i.e. it has a very good chance of actually getting optimized away). It's only a temporary band-aid, so I'm not overly attached, but nor did I want to spend too much time perfecting it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. I'd suggest fixing the underlying type of the enum so extent always has the same type:

enum : index_type { extent = Extent };

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, sounds good!

include/gsl/span Outdated
template <class ElementType, std::ptrdiff_t Extent, std::ptrdiff_t Offset, std::ptrdiff_t Count>
struct calculate_subspan_type
{
using type = span<ElementType, Count != dynamic_extent ? Count : (Extent != dynamic_extent ? Extent - Offset - 1 : Extent)>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extent - Offset - 1 means that given:

int data[5]{};
span<int, 5> s = data;

decltype(s.subspan<5>()) is span<int, -1> instead of span<int, 0>, and decltype(s.subspan<0>()) is span<int, 4> instead of span<int, 5>. Surely it should be an invariant that s.subspan<0>() == s for a span s. This expression should be Extent - Offset.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are completely right. Nice catch. That will teach me for working at night.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In passing...I took a look at the ranges implementation and it doesn't seem to match the intended design for the templated subspan: that people can pass a dynamic_extent value for the second template parameter to mean "consume everything that remains". (symmetrical to the way the non-template subspan works). Is that something you guys chose to do explicitly, or just an outcome of the implementation choice?

Copy link
Contributor

@CaseyCarter CaseyCarter Feb 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that something you guys chose to do explicitly, or just an outcome of the implementation choice?

That is a bug I introduced when I split subspan out into multiple members. It didn't occur to me that someone would explicitly specify dynamic_extent for the second template parameter instead of simply omitting it. It apparently didn't occur to you either, there's no test coverage of such a call to subspan. ;)

EDIT: Filed as ericniebler/range-v3#799.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, and thanks for the bug report!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hahahaha. I thought about the lack of test coverage just after posting the comment....I had a distinct memory you'd run these tests against the implementation...so it's all really my fault (spoiler alert: most things are).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And thanks for all the help! You came for the review, stayed for the bug report!

{
span<int, 5> av = arr;
CHECK((av.subspan<1>().size() == 3));
CHECK(decltype(av.subspan<1>())::extent == 3);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto for these two lines: decltype(av.subspan<1>())::extent should be 4.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! My brain clearly had extent and size confused.

@neilmacintosh neilmacintosh merged commit 6a33b97 into microsoft:master Mar 4, 2018
@neilmacintosh neilmacintosh deleted the fix-subspan-return branch March 4, 2018 03:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants