Commit c43f927a authored by Adam Langley's avatar Adam Langley Committed by Commit Bot

base: add EXTENT macro.

It's annoying that one cannot just call size() on a std::array in a
compile-time context. Instead std::tuple_size and std::remove_reference
is needed. While std::array is used quite a lot in Chromium,
std::tuple_size is not. I assume that people are either not writing
assertions, using run-time [D]CHECKs, or using static_assert with
constants that are also used as std::array lengths.

None the less, I cannot point at a body of std::tuple_size in the code
base that would be cleaned up by this, so the case for adding a #define
is not that strong. *I* would certainly use it in static_asserts though!
(And would change [D]CHECKs into static asserts.) Maybe others would
too.

A #define isn't as nice as a constexpr function, but the latter doesn't
work as nicely at the call site. Consider the obvious:

  template<typename T, size_t N> constexpr size_t std_array_size(const std::array<T, N>&) { return N; }

That works, but not for references, where the compiler objects that the
reference “is not a constant expression”, even though the value is never
used.

Other non-define functions can work if preferred, but are worse at the
callsite. For example:

  template<typename T> constexpr size_t std_array_size() {
    return std::tuple_size<typename std::remove_reference<T>::type>::value;
  }

But that has the callsite pattern:

  static_assert(base::std_array_size<decltype(array)>() == kFoo);

This this change uses base::span::extent to define a macro, EXTENT, that
works for any type that base::make_span recognises, including
std::array.

Change-Id: Id4688117b5a558e022448ad901fa3d4995182826
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2364136Reviewed-by: default avatarJan Wilken Dörrie <jdoerrie@chromium.org>
Commit-Queue: Adam Langley <agl@chromium.org>
Cr-Commit-Position: refs/heads/master@{#801142}
parent 5c443f1e
......@@ -132,6 +132,16 @@ struct ExtentStorage<dynamic_extent> {
size_t size_;
};
// must_not_be_dynamic_extent prevents |dynamic_extent| from being returned in a
// constexpr context.
template <size_t kExtent>
constexpr size_t must_not_be_dynamic_extent() {
static_assert(
kExtent != dynamic_extent,
"EXTENT should only be used for containers with a static extent.");
return kExtent;
}
} // namespace internal
// A span is a value type that represents an array of elements of type T. Since
......@@ -471,4 +481,15 @@ constexpr auto make_span(Container&& container) noexcept {
} // namespace base
// EXTENT returns the size of any type that can be converted to a |base::span|
// with definite extent, i.e. everything that is a contiguous storage of some
// sort with static size. Specifically, this works for std::array in a constexpr
// context. Note:
// * |base::size| should be preferred for plain arrays.
// * In run-time contexts, functions such as |std::array::size| should be
// preferred.
#define EXTENT(x) \
::base::internal::must_not_be_dynamic_extent<decltype( \
::base::make_span(x))::extent>()
#endif // BASE_CONTAINERS_SPAN_H_
......@@ -1425,4 +1425,25 @@ TEST(SpanTest, IteratorConversions) {
"Error: const iterator should not be convertible to iterator");
}
TEST(SpanTest, ExtentMacro) {
constexpr size_t kSize = 10;
std::array<uint8_t, kSize> array;
static_assert(EXTENT(array) == kSize, "EXTENT broken");
const std::array<uint8_t, kSize>& reference = array;
static_assert(EXTENT(reference) == kSize, "EXTENT broken for references");
const std::array<uint8_t, kSize>* pointer = nullptr;
static_assert(EXTENT(*pointer) == kSize, "EXTENT broken for pointers");
uint8_t plain_array[kSize] = {0};
static_assert(EXTENT(plain_array) == kSize, "EXTENT broken for plain arrays");
// EXTENT should not result in |dynamic_extent|, it should be a compile-time
// error. However, we don't have compile-failure tests.
//
// std::vector<uint8_t> vector;
// static_assert(EXTENT(vector) == dynamic_extent, "Should not compile");
}
} // namespace base
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment