[{"id":3900,"web_url":"https://patchwork.libcamera.org/comment/3900/","msgid":"<94575be9-a2b0-daa2-51fd-e922a7659343@ideasonboard.com>","date":"2020-03-02T22:21:03","subject":"Re: [libcamera-devel] [PATCH 01/31] libcamera: Add a\n\tC++20-compliant std::span<> implementation","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Hi Laurent / Jacopo,\n\nOn 29/02/2020 16:42, Laurent Pinchart wrote:\n> From: Jacopo Mondi <jacopo@jmondi.org>\n> \n> C++20 will contain a std::span<> class that represents a contiguous\n\nWill contain ? or contains ...\n\n(Yes, I know it depends on when you wrote that, but it might already be\nout-dated)\n\n> sequence of objects. \n\nHow about the following text from the C++20 draft:\n\n\"A span is a view over a contiguous sequence of objects, the storage of\nwhich is owned by some other object.\"\n\n>Its main purpose is to group array pointers and\n> size together in APIs.\n\n\"It provides a common interface to a contiguous collection of objects\nand supports both a static extent where the number of elements is known\nand stored as the size, and a dynamic extent where the number of\nelements is not known or restricted by the implementation.\"\n\n(Correct that where necessary or entirely)\n\n\n> \n> Add a compatible implementation to the utils namespace. This will be\n> used to implement array controls.\n> \n> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>\n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\nI'm not going to admit any kind of real Reviewed-by: on 417 lines of\ntemplate magic.\n\nBut if this is working, and follows the C++ interfaces, then (with the\ncommit message improved a little, and possibly dropping span.cpp, and\nassociated addition in src/libcamera/meson.build which seems pointless)\n\nAcked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n\n\n> ---\n>  Documentation/Doxyfile.in     |   4 +-\n>  include/libcamera/meson.build |   1 +\n>  include/libcamera/span.h      | 417 ++++++++++++++++++++++++++++++++++\n>  src/libcamera/meson.build     |   1 +\n\n>  src/libcamera/span.cpp        |  12 +\n\nDo we really need to add an empty span.cpp ?\n\n>  5 files changed, 434 insertions(+), 1 deletion(-)\n>  create mode 100644 include/libcamera/span.h\n>  create mode 100644 src/libcamera/span.cpp\n> \n> diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in\n> index beeaf6d3cf48..457e23a086a2 100644\n> --- a/Documentation/Doxyfile.in\n> +++ b/Documentation/Doxyfile.in\n> @@ -840,8 +840,10 @@ RECURSIVE              = YES\n>  # Note that relative paths are relative to the directory from which doxygen is\n>  # run.\n>  \n> -EXCLUDE                = @TOP_SRCDIR@/src/libcamera/device_enumerator_sysfs.cpp \\\n> +EXCLUDE                = @TOP_SRCDIR@/include/libcamera/span.h \\\n> +\t\t\t @TOP_SRCDIR@/src/libcamera/device_enumerator_sysfs.cpp \\\n>  \t\t\t @TOP_SRCDIR@/src/libcamera/device_enumerator_udev.cpp \\\n> +\t\t\t @TOP_SRCDIR@/src/libcamera/span.cpp \\\n\nWe even exclude this empty file from the doxygen build, so it really\ndoes seem pointless.\n\n\n>  \t\t\t @TOP_SRCDIR@/src/libcamera/include/device_enumerator_sysfs.h \\\n>  \t\t\t @TOP_SRCDIR@/src/libcamera/include/device_enumerator_udev.h \\\n>  \t\t\t @TOP_SRCDIR@/src/libcamera/pipeline/ \\\n> diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build\n> index f58c02d2cf35..f47c583cbbc0 100644\n> --- a/include/libcamera/meson.build\n> +++ b/include/libcamera/meson.build\n> @@ -14,6 +14,7 @@ libcamera_api = files([\n>      'pixelformats.h',\n>      'request.h',\n>      'signal.h',\n> +    'span.h',\n>      'stream.h',\n>      'timer.h',\n>  ])\n> diff --git a/include/libcamera/span.h b/include/libcamera/span.h\n> new file mode 100644\n> index 000000000000..513ddb432405\n> --- /dev/null\n> +++ b/include/libcamera/span.h\n> @@ -0,0 +1,417 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Google Inc.\n> + *\n> + * span.h - C++20 std::span<> implementation for C++11\n> + */\n> +\n> +#ifndef __LIBCAMERA_SPAN_H__\n> +#define __LIBCAMERA_SPAN_H__\n> +\n> +#include <array>\n> +#include <iterator>\n> +#include <limits>\n> +#include <stddef.h>\n> +#include <type_traits>\n> +\n> +namespace libcamera {\n> +\n> +static constexpr std::size_t dynamic_extent = std::numeric_limits<std::size_t>::max();\n> +\n> +template<typename T, std::size_t Extent = dynamic_extent>\n> +class Span;\n> +\n> +namespace details {\n> +\n> +template<typename U>\n> +struct is_array : public std::false_type {\n> +};\n> +\n> +template<typename U, std::size_t N>\n> +struct is_array<std::array<U, N>> : public std::true_type {\n> +};\n> +\n> +template<typename U>\n> +struct is_span : public std::false_type {\n> +};\n> +\n> +template<typename U, std::size_t Extent>\n> +struct is_span<Span<U, Extent>> : public std::true_type {\n> +};\n> +\n> +} /* namespace details */\n> +\n> +namespace utils {\n> +\n> +template<typename C>\n> +constexpr auto size(const C &c) -> decltype(c.size())\n> +{\n> +\treturn c.size();\n> +}\n> +\n> +template<typename C>\n> +constexpr auto data(const C &c) -> decltype(c.data())\n> +{\n> +\treturn c.data();\n> +}\n> +\n> +template<typename C>\n> +constexpr auto data(C &c) -> decltype(c.data())\n> +{\n> +\treturn c.data();\n> +}\n> +\n> +template<class T, std::size_t N>\n> +constexpr T *data(T (&array)[N]) noexcept\n> +{\n> +\treturn array;\n> +}\n> +\n> +template<std::size_t I, typename T>\n> +struct tuple_element;\n> +\n> +template<std::size_t I, typename T, std::size_t N>\n> +struct tuple_element<I, Span<T, N>> {\n> +\tusing type = T;\n> +};\n> +\n> +template<typename T>\n> +struct tuple_size;\n> +\n> +template<typename T, std::size_t N>\n> +struct tuple_size<Span<T, N>> : public std::integral_constant<std::size_t, N> {\n> +};\n> +\n> +template<typename T>\n> +struct tuple_size<Span<T, dynamic_extent>>;\n> +\n> +} /* namespace utils */\n> +\n> +template<typename T, std::size_t Extent>\n> +class Span\n> +{\n> +public:\n> +\tusing element_type = T;\n> +\tusing value_type = typename std::remove_cv_t<T>;\n> +\tusing size_type = std::size_t;\n> +\tusing difference_type = std::ptrdiff_t;\n> +\tusing pointer = T *;\n> +\tusing const_pointer = const T *;\n> +\tusing reference = T &;\n> +\tusing const_reference = const T &;\n> +\tusing iterator = pointer;\n> +\tusing const_iterator = const_pointer;\n> +\tusing reverse_iterator = std::reverse_iterator<iterator>;\n> +\tusing const_reverse_iterator = std::reverse_iterator<const_iterator>;\n> +\n> +\tstatic constexpr std::size_t extent = Extent;\n> +\n> +\ttemplate<bool Dependent = false,\n> +\t\t typename = std::enable_if_t<Dependent || Extent == 0>>\n> +\tconstexpr Span() noexcept\n> +\t\t: data_(nullptr)\n> +\t{\n> +\t}\n> +\n> +\tconstexpr Span(pointer ptr, size_type count)\n> +\t\t: data_(ptr)\n> +\t{\n> +\t}\n> +\n> +\tconstexpr Span(pointer first, pointer last)\n> +\t\t: data_(first)\n> +\t{\n> +\t}\n> +\n> +\ttemplate<std::size_t N>\n> +\tconstexpr Span(element_type (&arr)[N],\n> +\t\t       std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],\n> +\t\t\t\t\t\t\t    element_type (*)[]>::value &&\n> +\t\t\t\t\tN == Extent,\n> +\t\t\t\t\tstd::nullptr_t> = nullptr) noexcept\n> +\t\t: data_(arr)\n> +\t{\n> +\t}\n> +\n> +\ttemplate<std::size_t N>\n> +\tconstexpr Span(std::array<value_type, N> &arr,\n> +\t\t       std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],\n> +\t\t\t\t\t\t\t    element_type (*)[]>::value &&\n> +\t\t\t\t\tN == Extent,\n> +\t\t\t\t\tstd::nullptr_t> = nullptr) noexcept\n> +\t\t: data_(arr.data())\n> +\t{\n> +\t}\n> +\n> +\ttemplate<std::size_t N>\n> +\tconstexpr Span(const std::array<value_type, N> &arr,\n> +\t\t       std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],\n> +\t\t\t\t\t\t\t    element_type (*)[]>::value &&\n> +\t\t\t\t\tN == Extent,\n> +\t\t\t\t\tstd::nullptr_t> = nullptr) noexcept\n> +\t\t: data_(arr.data())\n> +\t{\n> +\t}\n> +\n> +\ttemplate<class Container>\n> +\tconstexpr Span(Container &cont,\n> +\t\t       std::enable_if_t<!details::is_span<Container>::value &&\n> +\t\t\t\t\t!details::is_array<Container>::value &&\n> +\t\t\t\t\t!std::is_array<Container>::value &&\n> +\t\t\t\t\tstd::is_convertible<std::remove_pointer_t<decltype(utils::data(cont))> (*)[],\n> +\t\t\t\t\t\t\t    element_type (*)[]>::value,\n> +\t\t\t\t\tstd::nullptr_t> = nullptr)\n> +\t\t: data_(utils::data(cont))\n> +\t{\n> +\t}\n> +\n> +\ttemplate<class Container>\n> +\tconstexpr Span(const Container &cont,\n> +\t\t       std::enable_if_t<!details::is_span<Container>::value &&\n> +\t\t\t\t\t!details::is_array<Container>::value &&\n> +\t\t\t\t\t!std::is_array<Container>::value &&\n> +\t\t\t\t\tstd::is_convertible<std::remove_pointer_t<decltype(utils::data(cont))> (*)[],\n> +\t\t\t\t\t\t\t    element_type (*)[]>::value,\n> +\t\t\t\t\tstd::nullptr_t> = nullptr)\n> +\t\t: data_(utils::data(cont))\n> +\t{\n> +\t\tstatic_assert(utils::size(cont) == Extent, \"Size mismatch\");\n> +\t}\n> +\n> +\ttemplate<class U, std::size_t N>\n> +\tconstexpr Span(const Span<U, N> &s,\n> +\t\t       std::enable_if_t<std::is_convertible<U (*)[], element_type (*)[]>::value &&\n> +\t\t\t\t\tN == Extent,\n> +\t\t\t\t\tstd::nullptr_t> = nullptr) noexcept\n> +\t\t: data_(s.data())\n> +\t{\n> +\t}\n> +\n> +\tconstexpr Span(const Span &other) noexcept = default;\n> +\n> +\tconstexpr Span &operator=(const Span &other) noexcept\n> +\t{\n> +\t\tdata_ = other.data_;\n> +\t\treturn *this;\n> +\t}\n> +\n> +\tconstexpr iterator begin() const { return data(); }\n> +\tconstexpr const_iterator cbegin() const { return begin(); }\n> +\tconstexpr iterator end() const { return data() + size(); }\n> +\tconstexpr const_iterator cend() const { return end(); }\n> +\tconstexpr reverse_iterator rbegin() const { return reverse_iterator(data() + size() - 1); }\n> +\tconstexpr const_reverse_iterator crbegin() const { return rbegin(); }\n> +\tconstexpr reverse_iterator rend() const { return reverse_iterator(data() - 1); }\n> +\tconstexpr const_reverse_iterator crend() const { return rend(); }\n> +\n> +\tconstexpr reference front() const { return *data(); }\n> +\tconstexpr reference back() const { return *(data() + size() - 1); }\n> +\tconstexpr reference operator[](size_type idx) const { return data()[idx]; }\n> +\tconstexpr pointer data() const noexcept { return data_; }\n> +\n> +\tconstexpr size_type size() const noexcept { return Extent; }\n> +\tconstexpr size_type size_bytes() const noexcept { return size() * sizeof(element_type); }\n> +\tconstexpr bool empty() const noexcept { return size() == 0; }\n> +\n> +\ttemplate<std::size_t Count>\n> +\tconstexpr Span<element_type, Count> first() const\n> +\t{\n> +\t\tstatic_assert(Count <= Extent, \"Count larger than size\");\n> +\t\treturn { data(), Count };\n> +\t}\n> +\n> +\tconstexpr Span<element_type, dynamic_extent> first(std::size_t Count) const\n> +\t{\n> +\t\treturn { data(), Count };\n> +\t}\n> +\n> +\ttemplate<std::size_t Count>\n> +\tconstexpr Span<element_type, Count> last() const\n> +\t{\n> +\t\tstatic_assert(Count <= Extent, \"Count larger than size\");\n> +\t\treturn { data() + size() - Count, Count };\n> +\t}\n> +\n> +\tconstexpr Span<element_type, dynamic_extent> last(std::size_t Count) const\n> +\t{\n> +\t\treturn { data() + size() - Count, Count };\n> +\t}\n> +\n> +\ttemplate<std::size_t Offset, std::size_t Count = dynamic_extent>\n> +\tconstexpr Span<element_type, Count != dynamic_extent ? Count : Extent - Offset> subspan() const\n> +\t{\n> +\t\tstatic_assert(Offset <= Extent, \"Offset larger than size\");\n> +\t\tstatic_assert(Count == dynamic_extent || Count + Offset <= Extent,\n> +\t\t\t      \"Offset + Count larger than size\");\n> +\t\treturn { data() + Offset, Count == dynamic_extent ? size() - Offset : Count };\n> +\t}\n> +\n> +\tconstexpr Span<element_type, dynamic_extent>\n> +\tsubspan(std::size_t Offset, std::size_t Count = dynamic_extent) const\n> +\t{\n> +\t\treturn { data() + Offset, Count == dynamic_extent ? size() - Offset : Count };\n> +\t}\n> +\n> +private:\n> +\tpointer data_;\n> +};\n> +\n> +template<typename T>\n> +class Span<T, dynamic_extent>\n> +{\n> +public:\n> +\tusing element_type = T;\n> +\tusing value_type = typename std::remove_cv_t<T>;\n> +\tusing size_type = std::size_t;\n> +\tusing difference_type = std::ptrdiff_t;\n> +\tusing pointer = T *;\n> +\tusing const_pointer = const T *;\n> +\tusing reference = T &;\n> +\tusing const_reference = const T &;\n> +\tusing iterator = T *;\n> +\tusing const_iterator = const T *;\n> +\tusing reverse_iterator = std::reverse_iterator<iterator>;\n> +\tusing const_reverse_iterator = std::reverse_iterator<const_iterator>;\n> +\n> +\tstatic constexpr std::size_t extent = dynamic_extent;\n> +\n> +\tconstexpr Span() noexcept\n> +\t\t: data_(nullptr), size_(0)\n> +\t{\n> +\t}\n> +\n> +\tconstexpr Span(pointer ptr, size_type count)\n> +\t\t: data_(ptr), size_(count)\n> +\t{\n> +\t}\n> +\n> +\tconstexpr Span(pointer first, pointer last)\n> +\t\t: data_(first), size_(last - first)\n> +\t{\n> +\t}\n> +\n> +\ttemplate<std::size_t N>\n> +\tconstexpr Span(element_type (&arr)[N],\n> +\t\t       std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],\n> +\t\t\t\t\t\t\t    element_type (*)[]>::value,\n> +\t\t\t\t\tstd::nullptr_t> = nullptr) noexcept\n> +\t\t: data_(arr), size_(N)\n> +\t{\n> +\t}\n> +\n> +\ttemplate<std::size_t N>\n> +\tconstexpr Span(std::array<value_type, N> &arr,\n> +\t\t       std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],\n> +\t\t\t\t\t\t\t    element_type (*)[]>::value,\n> +\t\t\t\t\tstd::nullptr_t> = nullptr) noexcept\n> +\t\t: data_(utils::data(arr)), size_(N)\n> +\t{\n> +\t}\n> +\n> +\ttemplate<std::size_t N>\n> +\tconstexpr Span(const std::array<value_type, N> &arr) noexcept\n> +\t\t: data_(utils::data(arr)), size_(N)\n> +\t{\n> +\t}\n> +\n> +\ttemplate<class Container>\n> +\tconstexpr Span(Container &cont,\n> +\t\t       std::enable_if_t<!details::is_span<Container>::value &&\n> +\t\t\t\t\t!details::is_array<Container>::value &&\n> +\t\t\t\t\t!std::is_array<Container>::value &&\n> +\t\t\t\t\tstd::is_convertible<std::remove_pointer_t<decltype(utils::data(cont))> (*)[],\n> +\t\t\t\t\t\t\t    element_type (*)[]>::value,\n> +\t\t\t\t\tstd::nullptr_t> = nullptr)\n> +\t\t: data_(utils::data(cont)), size_(utils::size(cont))\n> +\t{\n> +\t}\n> +\n> +\ttemplate<class Container>\n> +\tconstexpr Span(const Container &cont,\n> +\t\t       std::enable_if_t<!details::is_span<Container>::value &&\n> +\t\t\t\t\t!details::is_array<Container>::value &&\n> +\t\t\t\t\t!std::is_array<Container>::value &&\n> +\t\t\t\t\tstd::is_convertible<std::remove_pointer_t<decltype(utils::data(cont))> (*)[],\n> +\t\t\t\t\t\t\t    element_type (*)[]>::value,\n> +\t\t\t\t\tstd::nullptr_t> = nullptr)\n> +\t\t: data_(utils::data(cont)), size_(utils::size(cont))\n> +\t{\n> +\t}\n> +\n> +\ttemplate<class U, std::size_t N>\n> +\tconstexpr Span(const Span<U, N> &s,\n> +\t\t       std::enable_if_t<std::is_convertible<U (*)[], element_type (*)[]>::value,\n> +\t\t\t\t\tstd::nullptr_t> = nullptr) noexcept\n> +\t\t: data_(s.data()), size_(s.size())\n> +\t{\n> +\t}\n> +\n> +\tconstexpr Span(const Span &other) noexcept = default;\n> +\n> +\tconstexpr Span &operator=(const Span &other) noexcept\n> +\t{\n> +\t\tdata_ = other.data_;\n> +\t\tsize_ = other.size_;\n> +\t\treturn *this;\n> +\t}\n> +\n> +\tconstexpr iterator begin() const { return data(); }\n> +\tconstexpr const_iterator cbegin() const { return begin(); }\n> +\tconstexpr iterator end() const { return data() + size(); }\n> +\tconstexpr const_iterator cend() const { return end(); }\n> +\tconstexpr reverse_iterator rbegin() const { return reverse_iterator(data() + size() - 1); }\n> +\tconstexpr const_reverse_iterator crbegin() const { return rbegin(); }\n> +\tconstexpr reverse_iterator rend() const { return reverse_iterator(data() - 1); }\n> +\tconstexpr const_reverse_iterator crend() const { return rend(); }\n> +\n> +\tconstexpr reference front() const { return *data(); }\n> +\tconstexpr reference back() const { return *(data() + size() - 1); }\n> +\tconstexpr reference operator[](size_type idx) const { return data()[idx]; }\n> +\tconstexpr pointer data() const noexcept { return data_; }\n> +\n> +\tconstexpr size_type size() const noexcept { return size_; }\n> +\tconstexpr size_type size_bytes() const noexcept { return size() * sizeof(element_type); }\n> +\tconstexpr bool empty() const noexcept { return size() == 0; }\n> +\n> +\ttemplate<std::size_t Count>\n> +\tconstexpr Span<element_type, Count> first() const\n> +\t{\n> +\t\treturn { data(), Count };\n> +\t}\n> +\n> +\tconstexpr Span<element_type, dynamic_extent> first(std::size_t Count) const\n> +\t{\n> +\t\treturn { data(), Count };\n> +\t}\n> +\n> +\ttemplate<std::size_t Count>\n> +\tconstexpr Span<element_type, Count> last() const\n> +\t{\n> +\t\treturn { data() + size() - Count, Count };\n> +\t}\n> +\n> +\tconstexpr Span<element_type, dynamic_extent> last(std::size_t Count) const\n> +\t{\n> +\t\treturn { data() + size() - Count, Count };\n> +\t}\n> +\n> +\ttemplate<std::size_t Offset, std::size_t Count = dynamic_extent>\n> +\tconstexpr Span<element_type, Count> subspan() const\n> +\t{\n> +\t\treturn { data() + Offset, Count == dynamic_extent ? size() - Offset : Count };\n> +\t}\n> +\n> +\tconstexpr Span<element_type, dynamic_extent>\n> +\tsubspan(std::size_t Offset, std::size_t Count = dynamic_extent) const\n> +\t{\n> +\t\treturn { data() + Offset, Count == dynamic_extent ? size() - Offset : Count };\n> +\t}\n> +\n> +private:\n> +\tpointer data_;\n> +\tsize_type size_;\n> +};\n> +\n> +}; /* namespace libcamera */\n> +\n> +#endif /* __LIBCAMERA_SPAN_H__ */\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index 88658ac563f7..2448f0e96468 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -36,6 +36,7 @@ libcamera_sources = files([\n>      'request.cpp',\n>      'semaphore.cpp',\n>      'signal.cpp',\n> +    'span.cpp',\n>      'stream.cpp',\n>      'thread.cpp',\n>      'timer.cpp',\n> diff --git a/src/libcamera/span.cpp b/src/libcamera/span.cpp\n> new file mode 100644\n> index 000000000000..753104765cea\n> --- /dev/null\n> +++ b/src/libcamera/span.cpp\n> @@ -0,0 +1,12 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Google Inc.\n> + *\n> + * span.h - C++20 std::span<> implementation for C++11\n\n.h? But if this file is dropped then it goes away.\n\n\n> + */\n> +\n> +#include <libcamera/span.h>\n> +\n> +namespace libcamera {\n> +\n> +} /* namespace libcamera */\n> \n\n\nDo we actually need span.cpp at all?","headers":{"Return-Path":"<kieran.bingham@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 96E57627B6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  2 Mar 2020 23:21:08 +0100 (CET)","from [192.168.0.20]\n\t(cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id EF35754A;\n\tMon,  2 Mar 2020 23:21:07 +0100 (CET)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1583187668;\n\tbh=YFSx6klUbpjbylGr5Ztkn4p05U1k0UWCUoJLVoGQ6CE=;\n\th=Reply-To:Subject:To:References:From:Date:In-Reply-To:From;\n\tb=l33CMJT1NHMCc+5nC27jJiRQS8nlifXURrqw2M5ycPbU8qF/atYp3IkGai6UwChU7\n\tfwqVE7caRXBAWHkZBTec02X3sdfzibMUTfn5Hd7BxwVxwl18Q2Ev1zusEGBUiU6Eav\n\t4JyYiiscguEe2Qx5q+Wr4qJRULQ7XpfhRfznb7N8=","Reply-To":"kieran.bingham@ideasonboard.com","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20200229164254.23604-1-laurent.pinchart@ideasonboard.com>\n\t<20200229164254.23604-2-laurent.pinchart@ideasonboard.com>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Openpgp":"preference=signencrypt","Autocrypt":"addr=kieran.bingham@ideasonboard.com; keydata=\n\t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\n\tWZw01QYHU/GUV/zHJSFk","Organization":"Ideas on Board","Message-ID":"<94575be9-a2b0-daa2-51fd-e922a7659343@ideasonboard.com>","Date":"Mon, 2 Mar 2020 22:21:03 +0000","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101\n\tThunderbird/60.9.1","MIME-Version":"1.0","In-Reply-To":"<20200229164254.23604-2-laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [PATCH 01/31] libcamera: Add a\n\tC++20-compliant std::span<> implementation","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Mon, 02 Mar 2020 22:21:08 -0000"}},{"id":3921,"web_url":"https://patchwork.libcamera.org/comment/3921/","msgid":"<20200303162929.GA11333@pendragon.ideasonboard.com>","date":"2020-03-03T16:29:29","subject":"Re: [libcamera-devel] [PATCH 01/31] libcamera: Add a\n\tC++20-compliant std::span<> implementation","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Kieran,\n\nOn Mon, Mar 02, 2020 at 10:21:03PM +0000, Kieran Bingham wrote:\n> On 29/02/2020 16:42, Laurent Pinchart wrote:\n> > From: Jacopo Mondi <jacopo@jmondi.org>\n> > \n> > C++20 will contain a std::span<> class that represents a contiguous\n> \n> Will contain ? or contains ...\n> \n> (Yes, I know it depends on when you wrote that, but it might already be\n> out-dated)\n\n\"C++20 is the informal name for the revision of the ISO/IEC standard for\nthe C++ programming language expected to follow C++17. The standard got\ntechnically finalized by WG21 at the meeting in Prague in February 2020.\nThe standard is expected to be officially published after the end of the\nDIS ballot in May 2020.\"\n\nSo technically we're still correct :-)\n\n> > sequence of objects. \n> \n> How about the following text from the C++20 draft:\n> \n> \"A span is a view over a contiguous sequence of objects, the storage of\n> which is owned by some other object.\"\n> \n> >Its main purpose is to group array pointers and\n> > size together in APIs.\n> \n> \"It provides a common interface to a contiguous collection of objects\n> and supports both a static extent where the number of elements is known\n> and stored as the size, and a dynamic extent where the number of\n> elements is not known or restricted by the implementation.\"\n> \n> (Correct that where necessary or entirely)\n\nI've replaced the commit message with\n\n    libcamera: Add a C++20-compliant std::span<> implementation\n\n    C++20 will contain a std::span<> class that provides view over a\n    contiguous sequence of objects, the storage of which is owned by some\n    other object.\n\n    Add a compatible implementation to the utils namespace. This will be\n    used to implement array controls.\n\n> > Add a compatible implementation to the utils namespace. This will be\n> > used to implement array controls.\n> > \n> > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>\n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> \n> I'm not going to admit any kind of real Reviewed-by: on 417 lines of\n> template magic.\n> \n> But if this is working, and follows the C++ interfaces, then (with the\n> commit message improved a little, and possibly dropping span.cpp, and\n> associated addition in src/libcamera/meson.build which seems pointless)\n\nspan.cpp ensures that the header is self-contained. It's a bit pointless\nas the test case can do so too. I'll modify the test to include\nlibcamera/span.h first, and drop span.cpp here.\n\n> Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> \n> > ---\n> >  Documentation/Doxyfile.in     |   4 +-\n> >  include/libcamera/meson.build |   1 +\n> >  include/libcamera/span.h      | 417 ++++++++++++++++++++++++++++++++++\n> >  src/libcamera/meson.build     |   1 +\n> >  src/libcamera/span.cpp        |  12 +\n> \n> Do we really need to add an empty span.cpp ?\n> \n> >  5 files changed, 434 insertions(+), 1 deletion(-)\n> >  create mode 100644 include/libcamera/span.h\n> >  create mode 100644 src/libcamera/span.cpp\n> > \n> > diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in\n> > index beeaf6d3cf48..457e23a086a2 100644\n> > --- a/Documentation/Doxyfile.in\n> > +++ b/Documentation/Doxyfile.in\n> > @@ -840,8 +840,10 @@ RECURSIVE              = YES\n> >  # Note that relative paths are relative to the directory from which doxygen is\n> >  # run.\n> >  \n> > -EXCLUDE                = @TOP_SRCDIR@/src/libcamera/device_enumerator_sysfs.cpp \\\n> > +EXCLUDE                = @TOP_SRCDIR@/include/libcamera/span.h \\\n> > +\t\t\t @TOP_SRCDIR@/src/libcamera/device_enumerator_sysfs.cpp \\\n> >  \t\t\t @TOP_SRCDIR@/src/libcamera/device_enumerator_udev.cpp \\\n> > +\t\t\t @TOP_SRCDIR@/src/libcamera/span.cpp \\\n> \n> We even exclude this empty file from the doxygen build, so it really\n> does seem pointless.\n> \n> >  \t\t\t @TOP_SRCDIR@/src/libcamera/include/device_enumerator_sysfs.h \\\n> >  \t\t\t @TOP_SRCDIR@/src/libcamera/include/device_enumerator_udev.h \\\n> >  \t\t\t @TOP_SRCDIR@/src/libcamera/pipeline/ \\\n> > diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build\n> > index f58c02d2cf35..f47c583cbbc0 100644\n> > --- a/include/libcamera/meson.build\n> > +++ b/include/libcamera/meson.build\n> > @@ -14,6 +14,7 @@ libcamera_api = files([\n> >      'pixelformats.h',\n> >      'request.h',\n> >      'signal.h',\n> > +    'span.h',\n> >      'stream.h',\n> >      'timer.h',\n> >  ])\n> > diff --git a/include/libcamera/span.h b/include/libcamera/span.h\n> > new file mode 100644\n> > index 000000000000..513ddb432405\n> > --- /dev/null\n> > +++ b/include/libcamera/span.h\n> > @@ -0,0 +1,417 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2020, Google Inc.\n> > + *\n> > + * span.h - C++20 std::span<> implementation for C++11\n> > + */\n> > +\n> > +#ifndef __LIBCAMERA_SPAN_H__\n> > +#define __LIBCAMERA_SPAN_H__\n> > +\n> > +#include <array>\n> > +#include <iterator>\n> > +#include <limits>\n> > +#include <stddef.h>\n> > +#include <type_traits>\n> > +\n> > +namespace libcamera {\n> > +\n> > +static constexpr std::size_t dynamic_extent = std::numeric_limits<std::size_t>::max();\n> > +\n> > +template<typename T, std::size_t Extent = dynamic_extent>\n> > +class Span;\n> > +\n> > +namespace details {\n> > +\n> > +template<typename U>\n> > +struct is_array : public std::false_type {\n> > +};\n> > +\n> > +template<typename U, std::size_t N>\n> > +struct is_array<std::array<U, N>> : public std::true_type {\n> > +};\n> > +\n> > +template<typename U>\n> > +struct is_span : public std::false_type {\n> > +};\n> > +\n> > +template<typename U, std::size_t Extent>\n> > +struct is_span<Span<U, Extent>> : public std::true_type {\n> > +};\n> > +\n> > +} /* namespace details */\n> > +\n> > +namespace utils {\n> > +\n> > +template<typename C>\n> > +constexpr auto size(const C &c) -> decltype(c.size())\n> > +{\n> > +\treturn c.size();\n> > +}\n> > +\n> > +template<typename C>\n> > +constexpr auto data(const C &c) -> decltype(c.data())\n> > +{\n> > +\treturn c.data();\n> > +}\n> > +\n> > +template<typename C>\n> > +constexpr auto data(C &c) -> decltype(c.data())\n> > +{\n> > +\treturn c.data();\n> > +}\n> > +\n> > +template<class T, std::size_t N>\n> > +constexpr T *data(T (&array)[N]) noexcept\n> > +{\n> > +\treturn array;\n> > +}\n> > +\n> > +template<std::size_t I, typename T>\n> > +struct tuple_element;\n> > +\n> > +template<std::size_t I, typename T, std::size_t N>\n> > +struct tuple_element<I, Span<T, N>> {\n> > +\tusing type = T;\n> > +};\n> > +\n> > +template<typename T>\n> > +struct tuple_size;\n> > +\n> > +template<typename T, std::size_t N>\n> > +struct tuple_size<Span<T, N>> : public std::integral_constant<std::size_t, N> {\n> > +};\n> > +\n> > +template<typename T>\n> > +struct tuple_size<Span<T, dynamic_extent>>;\n> > +\n> > +} /* namespace utils */\n> > +\n> > +template<typename T, std::size_t Extent>\n> > +class Span\n> > +{\n> > +public:\n> > +\tusing element_type = T;\n> > +\tusing value_type = typename std::remove_cv_t<T>;\n> > +\tusing size_type = std::size_t;\n> > +\tusing difference_type = std::ptrdiff_t;\n> > +\tusing pointer = T *;\n> > +\tusing const_pointer = const T *;\n> > +\tusing reference = T &;\n> > +\tusing const_reference = const T &;\n> > +\tusing iterator = pointer;\n> > +\tusing const_iterator = const_pointer;\n> > +\tusing reverse_iterator = std::reverse_iterator<iterator>;\n> > +\tusing const_reverse_iterator = std::reverse_iterator<const_iterator>;\n> > +\n> > +\tstatic constexpr std::size_t extent = Extent;\n> > +\n> > +\ttemplate<bool Dependent = false,\n> > +\t\t typename = std::enable_if_t<Dependent || Extent == 0>>\n> > +\tconstexpr Span() noexcept\n> > +\t\t: data_(nullptr)\n> > +\t{\n> > +\t}\n> > +\n> > +\tconstexpr Span(pointer ptr, size_type count)\n> > +\t\t: data_(ptr)\n> > +\t{\n> > +\t}\n> > +\n> > +\tconstexpr Span(pointer first, pointer last)\n> > +\t\t: data_(first)\n> > +\t{\n> > +\t}\n> > +\n> > +\ttemplate<std::size_t N>\n> > +\tconstexpr Span(element_type (&arr)[N],\n> > +\t\t       std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],\n> > +\t\t\t\t\t\t\t    element_type (*)[]>::value &&\n> > +\t\t\t\t\tN == Extent,\n> > +\t\t\t\t\tstd::nullptr_t> = nullptr) noexcept\n> > +\t\t: data_(arr)\n> > +\t{\n> > +\t}\n> > +\n> > +\ttemplate<std::size_t N>\n> > +\tconstexpr Span(std::array<value_type, N> &arr,\n> > +\t\t       std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],\n> > +\t\t\t\t\t\t\t    element_type (*)[]>::value &&\n> > +\t\t\t\t\tN == Extent,\n> > +\t\t\t\t\tstd::nullptr_t> = nullptr) noexcept\n> > +\t\t: data_(arr.data())\n> > +\t{\n> > +\t}\n> > +\n> > +\ttemplate<std::size_t N>\n> > +\tconstexpr Span(const std::array<value_type, N> &arr,\n> > +\t\t       std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],\n> > +\t\t\t\t\t\t\t    element_type (*)[]>::value &&\n> > +\t\t\t\t\tN == Extent,\n> > +\t\t\t\t\tstd::nullptr_t> = nullptr) noexcept\n> > +\t\t: data_(arr.data())\n> > +\t{\n> > +\t}\n> > +\n> > +\ttemplate<class Container>\n> > +\tconstexpr Span(Container &cont,\n> > +\t\t       std::enable_if_t<!details::is_span<Container>::value &&\n> > +\t\t\t\t\t!details::is_array<Container>::value &&\n> > +\t\t\t\t\t!std::is_array<Container>::value &&\n> > +\t\t\t\t\tstd::is_convertible<std::remove_pointer_t<decltype(utils::data(cont))> (*)[],\n> > +\t\t\t\t\t\t\t    element_type (*)[]>::value,\n> > +\t\t\t\t\tstd::nullptr_t> = nullptr)\n> > +\t\t: data_(utils::data(cont))\n> > +\t{\n> > +\t}\n> > +\n> > +\ttemplate<class Container>\n> > +\tconstexpr Span(const Container &cont,\n> > +\t\t       std::enable_if_t<!details::is_span<Container>::value &&\n> > +\t\t\t\t\t!details::is_array<Container>::value &&\n> > +\t\t\t\t\t!std::is_array<Container>::value &&\n> > +\t\t\t\t\tstd::is_convertible<std::remove_pointer_t<decltype(utils::data(cont))> (*)[],\n> > +\t\t\t\t\t\t\t    element_type (*)[]>::value,\n> > +\t\t\t\t\tstd::nullptr_t> = nullptr)\n> > +\t\t: data_(utils::data(cont))\n> > +\t{\n> > +\t\tstatic_assert(utils::size(cont) == Extent, \"Size mismatch\");\n> > +\t}\n> > +\n> > +\ttemplate<class U, std::size_t N>\n> > +\tconstexpr Span(const Span<U, N> &s,\n> > +\t\t       std::enable_if_t<std::is_convertible<U (*)[], element_type (*)[]>::value &&\n> > +\t\t\t\t\tN == Extent,\n> > +\t\t\t\t\tstd::nullptr_t> = nullptr) noexcept\n> > +\t\t: data_(s.data())\n> > +\t{\n> > +\t}\n> > +\n> > +\tconstexpr Span(const Span &other) noexcept = default;\n> > +\n> > +\tconstexpr Span &operator=(const Span &other) noexcept\n> > +\t{\n> > +\t\tdata_ = other.data_;\n> > +\t\treturn *this;\n> > +\t}\n> > +\n> > +\tconstexpr iterator begin() const { return data(); }\n> > +\tconstexpr const_iterator cbegin() const { return begin(); }\n> > +\tconstexpr iterator end() const { return data() + size(); }\n> > +\tconstexpr const_iterator cend() const { return end(); }\n> > +\tconstexpr reverse_iterator rbegin() const { return reverse_iterator(data() + size() - 1); }\n> > +\tconstexpr const_reverse_iterator crbegin() const { return rbegin(); }\n> > +\tconstexpr reverse_iterator rend() const { return reverse_iterator(data() - 1); }\n> > +\tconstexpr const_reverse_iterator crend() const { return rend(); }\n> > +\n> > +\tconstexpr reference front() const { return *data(); }\n> > +\tconstexpr reference back() const { return *(data() + size() - 1); }\n> > +\tconstexpr reference operator[](size_type idx) const { return data()[idx]; }\n> > +\tconstexpr pointer data() const noexcept { return data_; }\n> > +\n> > +\tconstexpr size_type size() const noexcept { return Extent; }\n> > +\tconstexpr size_type size_bytes() const noexcept { return size() * sizeof(element_type); }\n> > +\tconstexpr bool empty() const noexcept { return size() == 0; }\n> > +\n> > +\ttemplate<std::size_t Count>\n> > +\tconstexpr Span<element_type, Count> first() const\n> > +\t{\n> > +\t\tstatic_assert(Count <= Extent, \"Count larger than size\");\n> > +\t\treturn { data(), Count };\n> > +\t}\n> > +\n> > +\tconstexpr Span<element_type, dynamic_extent> first(std::size_t Count) const\n> > +\t{\n> > +\t\treturn { data(), Count };\n> > +\t}\n> > +\n> > +\ttemplate<std::size_t Count>\n> > +\tconstexpr Span<element_type, Count> last() const\n> > +\t{\n> > +\t\tstatic_assert(Count <= Extent, \"Count larger than size\");\n> > +\t\treturn { data() + size() - Count, Count };\n> > +\t}\n> > +\n> > +\tconstexpr Span<element_type, dynamic_extent> last(std::size_t Count) const\n> > +\t{\n> > +\t\treturn { data() + size() - Count, Count };\n> > +\t}\n> > +\n> > +\ttemplate<std::size_t Offset, std::size_t Count = dynamic_extent>\n> > +\tconstexpr Span<element_type, Count != dynamic_extent ? Count : Extent - Offset> subspan() const\n> > +\t{\n> > +\t\tstatic_assert(Offset <= Extent, \"Offset larger than size\");\n> > +\t\tstatic_assert(Count == dynamic_extent || Count + Offset <= Extent,\n> > +\t\t\t      \"Offset + Count larger than size\");\n> > +\t\treturn { data() + Offset, Count == dynamic_extent ? size() - Offset : Count };\n> > +\t}\n> > +\n> > +\tconstexpr Span<element_type, dynamic_extent>\n> > +\tsubspan(std::size_t Offset, std::size_t Count = dynamic_extent) const\n> > +\t{\n> > +\t\treturn { data() + Offset, Count == dynamic_extent ? size() - Offset : Count };\n> > +\t}\n> > +\n> > +private:\n> > +\tpointer data_;\n> > +};\n> > +\n> > +template<typename T>\n> > +class Span<T, dynamic_extent>\n> > +{\n> > +public:\n> > +\tusing element_type = T;\n> > +\tusing value_type = typename std::remove_cv_t<T>;\n> > +\tusing size_type = std::size_t;\n> > +\tusing difference_type = std::ptrdiff_t;\n> > +\tusing pointer = T *;\n> > +\tusing const_pointer = const T *;\n> > +\tusing reference = T &;\n> > +\tusing const_reference = const T &;\n> > +\tusing iterator = T *;\n> > +\tusing const_iterator = const T *;\n> > +\tusing reverse_iterator = std::reverse_iterator<iterator>;\n> > +\tusing const_reverse_iterator = std::reverse_iterator<const_iterator>;\n> > +\n> > +\tstatic constexpr std::size_t extent = dynamic_extent;\n> > +\n> > +\tconstexpr Span() noexcept\n> > +\t\t: data_(nullptr), size_(0)\n> > +\t{\n> > +\t}\n> > +\n> > +\tconstexpr Span(pointer ptr, size_type count)\n> > +\t\t: data_(ptr), size_(count)\n> > +\t{\n> > +\t}\n> > +\n> > +\tconstexpr Span(pointer first, pointer last)\n> > +\t\t: data_(first), size_(last - first)\n> > +\t{\n> > +\t}\n> > +\n> > +\ttemplate<std::size_t N>\n> > +\tconstexpr Span(element_type (&arr)[N],\n> > +\t\t       std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],\n> > +\t\t\t\t\t\t\t    element_type (*)[]>::value,\n> > +\t\t\t\t\tstd::nullptr_t> = nullptr) noexcept\n> > +\t\t: data_(arr), size_(N)\n> > +\t{\n> > +\t}\n> > +\n> > +\ttemplate<std::size_t N>\n> > +\tconstexpr Span(std::array<value_type, N> &arr,\n> > +\t\t       std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],\n> > +\t\t\t\t\t\t\t    element_type (*)[]>::value,\n> > +\t\t\t\t\tstd::nullptr_t> = nullptr) noexcept\n> > +\t\t: data_(utils::data(arr)), size_(N)\n> > +\t{\n> > +\t}\n> > +\n> > +\ttemplate<std::size_t N>\n> > +\tconstexpr Span(const std::array<value_type, N> &arr) noexcept\n> > +\t\t: data_(utils::data(arr)), size_(N)\n> > +\t{\n> > +\t}\n> > +\n> > +\ttemplate<class Container>\n> > +\tconstexpr Span(Container &cont,\n> > +\t\t       std::enable_if_t<!details::is_span<Container>::value &&\n> > +\t\t\t\t\t!details::is_array<Container>::value &&\n> > +\t\t\t\t\t!std::is_array<Container>::value &&\n> > +\t\t\t\t\tstd::is_convertible<std::remove_pointer_t<decltype(utils::data(cont))> (*)[],\n> > +\t\t\t\t\t\t\t    element_type (*)[]>::value,\n> > +\t\t\t\t\tstd::nullptr_t> = nullptr)\n> > +\t\t: data_(utils::data(cont)), size_(utils::size(cont))\n> > +\t{\n> > +\t}\n> > +\n> > +\ttemplate<class Container>\n> > +\tconstexpr Span(const Container &cont,\n> > +\t\t       std::enable_if_t<!details::is_span<Container>::value &&\n> > +\t\t\t\t\t!details::is_array<Container>::value &&\n> > +\t\t\t\t\t!std::is_array<Container>::value &&\n> > +\t\t\t\t\tstd::is_convertible<std::remove_pointer_t<decltype(utils::data(cont))> (*)[],\n> > +\t\t\t\t\t\t\t    element_type (*)[]>::value,\n> > +\t\t\t\t\tstd::nullptr_t> = nullptr)\n> > +\t\t: data_(utils::data(cont)), size_(utils::size(cont))\n> > +\t{\n> > +\t}\n> > +\n> > +\ttemplate<class U, std::size_t N>\n> > +\tconstexpr Span(const Span<U, N> &s,\n> > +\t\t       std::enable_if_t<std::is_convertible<U (*)[], element_type (*)[]>::value,\n> > +\t\t\t\t\tstd::nullptr_t> = nullptr) noexcept\n> > +\t\t: data_(s.data()), size_(s.size())\n> > +\t{\n> > +\t}\n> > +\n> > +\tconstexpr Span(const Span &other) noexcept = default;\n> > +\n> > +\tconstexpr Span &operator=(const Span &other) noexcept\n> > +\t{\n> > +\t\tdata_ = other.data_;\n> > +\t\tsize_ = other.size_;\n> > +\t\treturn *this;\n> > +\t}\n> > +\n> > +\tconstexpr iterator begin() const { return data(); }\n> > +\tconstexpr const_iterator cbegin() const { return begin(); }\n> > +\tconstexpr iterator end() const { return data() + size(); }\n> > +\tconstexpr const_iterator cend() const { return end(); }\n> > +\tconstexpr reverse_iterator rbegin() const { return reverse_iterator(data() + size() - 1); }\n> > +\tconstexpr const_reverse_iterator crbegin() const { return rbegin(); }\n> > +\tconstexpr reverse_iterator rend() const { return reverse_iterator(data() - 1); }\n> > +\tconstexpr const_reverse_iterator crend() const { return rend(); }\n> > +\n> > +\tconstexpr reference front() const { return *data(); }\n> > +\tconstexpr reference back() const { return *(data() + size() - 1); }\n> > +\tconstexpr reference operator[](size_type idx) const { return data()[idx]; }\n> > +\tconstexpr pointer data() const noexcept { return data_; }\n> > +\n> > +\tconstexpr size_type size() const noexcept { return size_; }\n> > +\tconstexpr size_type size_bytes() const noexcept { return size() * sizeof(element_type); }\n> > +\tconstexpr bool empty() const noexcept { return size() == 0; }\n> > +\n> > +\ttemplate<std::size_t Count>\n> > +\tconstexpr Span<element_type, Count> first() const\n> > +\t{\n> > +\t\treturn { data(), Count };\n> > +\t}\n> > +\n> > +\tconstexpr Span<element_type, dynamic_extent> first(std::size_t Count) const\n> > +\t{\n> > +\t\treturn { data(), Count };\n> > +\t}\n> > +\n> > +\ttemplate<std::size_t Count>\n> > +\tconstexpr Span<element_type, Count> last() const\n> > +\t{\n> > +\t\treturn { data() + size() - Count, Count };\n> > +\t}\n> > +\n> > +\tconstexpr Span<element_type, dynamic_extent> last(std::size_t Count) const\n> > +\t{\n> > +\t\treturn { data() + size() - Count, Count };\n> > +\t}\n> > +\n> > +\ttemplate<std::size_t Offset, std::size_t Count = dynamic_extent>\n> > +\tconstexpr Span<element_type, Count> subspan() const\n> > +\t{\n> > +\t\treturn { data() + Offset, Count == dynamic_extent ? size() - Offset : Count };\n> > +\t}\n> > +\n> > +\tconstexpr Span<element_type, dynamic_extent>\n> > +\tsubspan(std::size_t Offset, std::size_t Count = dynamic_extent) const\n> > +\t{\n> > +\t\treturn { data() + Offset, Count == dynamic_extent ? size() - Offset : Count };\n> > +\t}\n> > +\n> > +private:\n> > +\tpointer data_;\n> > +\tsize_type size_;\n> > +};\n> > +\n> > +}; /* namespace libcamera */\n> > +\n> > +#endif /* __LIBCAMERA_SPAN_H__ */\n> > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> > index 88658ac563f7..2448f0e96468 100644\n> > --- a/src/libcamera/meson.build\n> > +++ b/src/libcamera/meson.build\n> > @@ -36,6 +36,7 @@ libcamera_sources = files([\n> >      'request.cpp',\n> >      'semaphore.cpp',\n> >      'signal.cpp',\n> > +    'span.cpp',\n> >      'stream.cpp',\n> >      'thread.cpp',\n> >      'timer.cpp',\n> > diff --git a/src/libcamera/span.cpp b/src/libcamera/span.cpp\n> > new file mode 100644\n> > index 000000000000..753104765cea\n> > --- /dev/null\n> > +++ b/src/libcamera/span.cpp\n> > @@ -0,0 +1,12 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2020, Google Inc.\n> > + *\n> > + * span.h - C++20 std::span<> implementation for C++11\n> \n> .h? But if this file is dropped then it goes away.\n> \n> > + */\n> > +\n> > +#include <libcamera/span.h>\n> > +\n> > +namespace libcamera {\n> > +\n> > +} /* namespace libcamera */\n> > \n> \n> Do we actually need span.cpp at all?","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 616F260427\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  3 Mar 2020 17:29:51 +0100 (CET)","from pendragon.ideasonboard.com (81-175-216-236.bb.dnainternet.fi\n\t[81.175.216.236])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id A926D54A;\n\tTue,  3 Mar 2020 17:29:50 +0100 (CET)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1583252990;\n\tbh=BoAVFltFCApIiyr10/lSL3TYHZjmmwPm0X8iyRaQvdc=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=ERnxJg7ZUh3MrQnWnK98cwIBWEcMXo/xvLdvXHss+lkBYNGMQee3B5rkgYVM5VNfY\n\tDcGTgc1dd6fooqSsWNeuWl0ldUycUVeC0kcWs/q1T6+01slDetQuynMoPdw/5NmfNK\n\tkChl9QTqziTPbdf6CpDa7btLmVlS83iVsX8pFRFE=","Date":"Tue, 3 Mar 2020 18:29:29 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20200303162929.GA11333@pendragon.ideasonboard.com>","References":"<20200229164254.23604-1-laurent.pinchart@ideasonboard.com>\n\t<20200229164254.23604-2-laurent.pinchart@ideasonboard.com>\n\t<94575be9-a2b0-daa2-51fd-e922a7659343@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<94575be9-a2b0-daa2-51fd-e922a7659343@ideasonboard.com>","User-Agent":"Mutt/1.10.1 (2018-07-13)","Subject":"Re: [libcamera-devel] [PATCH 01/31] libcamera: Add a\n\tC++20-compliant std::span<> implementation","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Tue, 03 Mar 2020 16:29:51 -0000"}}]