aboutsummaryrefslogtreecommitdiff
path: root/pw_kvs/public/pw_kvs/internal/span_traits.h
blob: f85f0d60ce126eb8c5473af7cad68617bbb6966d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// Copyright 2020 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once

#include <iterator>
#include <type_traits>

namespace pw {
namespace kvs {
namespace internal {

// This borrows the `make_span` function from Chromium and uses to see if a type
// can be represented as a span. See:
// https://chromium.googlesource.com/chromium/src/+/master/base/containers/span.h

// Simplified implementation of C++20's std::iter_reference_t.
// As opposed to std::iter_reference_t, this implementation does not restrict
// the type of `Iter`.
//
// Reference: https://wg21.link/iterator.synopsis#:~:text=iter_reference_t
template <typename Iter>
using iter_reference_t = decltype(*std::declval<Iter&>());

template <typename T>
struct ExtentImpl : std::integral_constant<size_t, std::dynamic_extent> {};

template <typename T, size_t N>
struct ExtentImpl<T[N]> : std::integral_constant<size_t, N> {};

template <typename T, size_t N>
struct ExtentImpl<std::array<T, N>> : std::integral_constant<size_t, N> {};

template <typename T, size_t N>
struct ExtentImpl<std::span<T, N>> : std::integral_constant<size_t, N> {};

template <typename T>
using Extent = ExtentImpl<std::remove_cv_t<std::remove_reference_t<T>>>;

// Type-deducing helpers for constructing a span.
template <int&... ExplicitArgumentBarrier, typename It, typename EndOrSize>
constexpr auto make_span(It it, EndOrSize end_or_size) noexcept {
  using T = std::remove_reference_t<iter_reference_t<It>>;
  return std::span<T>(it, end_or_size);
}

// make_span utility function that deduces both the span's value_type and extent
// from the passed in argument.
//
// Usage: auto span = base::make_span(...);
template <int&... ExplicitArgumentBarrier,
          typename Container,
          typename T = std::remove_pointer_t<
              decltype(std::data(std::declval<Container>()))>>
constexpr auto make_span(Container&& container) noexcept {
  return std::span<T, Extent<Container>::value>(
      std::forward<Container>(container));
}

// The make_span functions above don't seem to work correctly with arrays of
// non-const values, so add const to the type. That is fine for KVS's Put
// method, since the values can be accepted as const.
template <typename T,
          typename = decltype(make_span(std::declval<std::add_const_t<T>>()))>
constexpr bool ConvertsToSpan(int) {
  return true;
}

// If the expression std::span(T) fails, then the type can't be converted to a
// std::span.
template <typename T>
constexpr bool ConvertsToSpan(...) {
  return false;
}

}  // namespace internal

// Traits class to detect if the type converts to a std::span.
template <typename T>
struct ConvertsToSpan
    : public std::bool_constant<
          internal::ConvertsToSpan<std::remove_reference_t<T>>(0)> {};

}  // namespace kvs
}  // namespace pw