1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include <iterator>
17 #include <type_traits>
18 
19 namespace pw {
20 namespace kvs {
21 namespace internal {
22 
23 // This borrows the `make_span` function from Chromium and uses to see if a type
24 // can be represented as a span. See:
25 // https://chromium.googlesource.com/chromium/src/+/master/base/containers/span.h
26 
27 // Simplified implementation of C++20's std::iter_reference_t.
28 // As opposed to std::iter_reference_t, this implementation does not restrict
29 // the type of `Iter`.
30 //
31 // Reference: https://wg21.link/iterator.synopsis#:~:text=iter_reference_t
32 template <typename Iter>
33 using iter_reference_t = decltype(*std::declval<Iter&>());
34 
35 template <typename T>
36 struct ExtentImpl : std::integral_constant<size_t, std::dynamic_extent> {};
37 
38 template <typename T, size_t N>
39 struct ExtentImpl<T[N]> : std::integral_constant<size_t, N> {};
40 
41 template <typename T, size_t N>
42 struct ExtentImpl<std::array<T, N>> : std::integral_constant<size_t, N> {};
43 
44 template <typename T, size_t N>
45 struct ExtentImpl<std::span<T, N>> : std::integral_constant<size_t, N> {};
46 
47 template <typename T>
48 using Extent = ExtentImpl<std::remove_cv_t<std::remove_reference_t<T>>>;
49 
50 // Type-deducing helpers for constructing a span.
51 template <int&... ExplicitArgumentBarrier, typename It, typename EndOrSize>
52 constexpr auto make_span(It it, EndOrSize end_or_size) noexcept {
53   using T = std::remove_reference_t<iter_reference_t<It>>;
54   return std::span<T>(it, end_or_size);
55 }
56 
57 // make_span utility function that deduces both the span's value_type and extent
58 // from the passed in argument.
59 //
60 // Usage: auto span = base::make_span(...);
61 template <int&... ExplicitArgumentBarrier,
62           typename Container,
63           typename T = std::remove_pointer_t<
64               decltype(std::data(std::declval<Container>()))>>
65 constexpr auto make_span(Container&& container) noexcept {
66   return std::span<T, Extent<Container>::value>(
67       std::forward<Container>(container));
68 }
69 
70 // The make_span functions above don't seem to work correctly with arrays of
71 // non-const values, so add const to the type. That is fine for KVS's Put
72 // method, since the values can be accepted as const.
73 template <typename T,
74           typename = decltype(make_span(std::declval<std::add_const_t<T>>()))>
75 constexpr bool ConvertsToSpan(int) {
76   return true;
77 }
78 
79 // If the expression std::span(T) fails, then the type can't be converted to a
80 // std::span.
81 template <typename T>
82 constexpr bool ConvertsToSpan(...) {
83   return false;
84 }
85 
86 }  // namespace internal
87 
88 // Traits class to detect if the type converts to a std::span.
89 template <typename T>
90 struct ConvertsToSpan
91     : public std::bool_constant<
92           internal::ConvertsToSpan<std::remove_reference_t<T>>(0)> {};
93 
94 }  // namespace kvs
95 }  // namespace pw
96