[libcamera-devel] [PATCH 01/31] libcamera: Add a C++20-compliant std::span<> implementation

Kieran Bingham kieran.bingham at ideasonboard.com
Mon Mar 2 23:21:03 CET 2020


Hi Laurent / Jacopo,

On 29/02/2020 16:42, Laurent Pinchart wrote:
> From: Jacopo Mondi <jacopo at jmondi.org>
> 
> C++20 will contain a std::span<> class that represents a contiguous

Will contain ? or contains ...

(Yes, I know it depends on when you wrote that, but it might already be
out-dated)

> sequence of objects. 

How about the following text from the C++20 draft:

"A span is a view over a contiguous sequence of objects, the storage of
which is owned by some other object."

>Its main purpose is to group array pointers and
> size together in APIs.

"It provides a common interface to a contiguous collection of objects
and supports both a static extent where the number of elements is known
and stored as the size, and a dynamic extent where the number of
elements is not known or restricted by the implementation."

(Correct that where necessary or entirely)


> 
> Add a compatible implementation to the utils namespace. This will be
> used to implement array controls.
> 
> Signed-off-by: Jacopo Mondi <jacopo at jmondi.org>
> Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>

I'm not going to admit any kind of real Reviewed-by: on 417 lines of
template magic.

But if this is working, and follows the C++ interfaces, then (with the
commit message improved a little, and possibly dropping span.cpp, and
associated addition in src/libcamera/meson.build which seems pointless)

Acked-by: Kieran Bingham <kieran.bingham at ideasonboard.com>



> ---
>  Documentation/Doxyfile.in     |   4 +-
>  include/libcamera/meson.build |   1 +
>  include/libcamera/span.h      | 417 ++++++++++++++++++++++++++++++++++
>  src/libcamera/meson.build     |   1 +

>  src/libcamera/span.cpp        |  12 +

Do we really need to add an empty span.cpp ?

>  5 files changed, 434 insertions(+), 1 deletion(-)
>  create mode 100644 include/libcamera/span.h
>  create mode 100644 src/libcamera/span.cpp
> 
> diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in
> index beeaf6d3cf48..457e23a086a2 100644
> --- a/Documentation/Doxyfile.in
> +++ b/Documentation/Doxyfile.in
> @@ -840,8 +840,10 @@ RECURSIVE              = YES
>  # Note that relative paths are relative to the directory from which doxygen is
>  # run.
>  
> -EXCLUDE                = @TOP_SRCDIR@/src/libcamera/device_enumerator_sysfs.cpp \
> +EXCLUDE                = @TOP_SRCDIR@/include/libcamera/span.h \
> +			 @TOP_SRCDIR@/src/libcamera/device_enumerator_sysfs.cpp \
>  			 @TOP_SRCDIR@/src/libcamera/device_enumerator_udev.cpp \
> +			 @TOP_SRCDIR@/src/libcamera/span.cpp \

We even exclude this empty file from the doxygen build, so it really
does seem pointless.


>  			 @TOP_SRCDIR@/src/libcamera/include/device_enumerator_sysfs.h \
>  			 @TOP_SRCDIR@/src/libcamera/include/device_enumerator_udev.h \
>  			 @TOP_SRCDIR@/src/libcamera/pipeline/ \
> diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build
> index f58c02d2cf35..f47c583cbbc0 100644
> --- a/include/libcamera/meson.build
> +++ b/include/libcamera/meson.build
> @@ -14,6 +14,7 @@ libcamera_api = files([
>      'pixelformats.h',
>      'request.h',
>      'signal.h',
> +    'span.h',
>      'stream.h',
>      'timer.h',
>  ])
> diff --git a/include/libcamera/span.h b/include/libcamera/span.h
> new file mode 100644
> index 000000000000..513ddb432405
> --- /dev/null
> +++ b/include/libcamera/span.h
> @@ -0,0 +1,417 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2020, Google Inc.
> + *
> + * span.h - C++20 std::span<> implementation for C++11
> + */
> +
> +#ifndef __LIBCAMERA_SPAN_H__
> +#define __LIBCAMERA_SPAN_H__
> +
> +#include <array>
> +#include <iterator>
> +#include <limits>
> +#include <stddef.h>
> +#include <type_traits>
> +
> +namespace libcamera {
> +
> +static constexpr std::size_t dynamic_extent = std::numeric_limits<std::size_t>::max();
> +
> +template<typename T, std::size_t Extent = dynamic_extent>
> +class Span;
> +
> +namespace details {
> +
> +template<typename U>
> +struct is_array : public std::false_type {
> +};
> +
> +template<typename U, std::size_t N>
> +struct is_array<std::array<U, N>> : public std::true_type {
> +};
> +
> +template<typename U>
> +struct is_span : public std::false_type {
> +};
> +
> +template<typename U, std::size_t Extent>
> +struct is_span<Span<U, Extent>> : public std::true_type {
> +};
> +
> +} /* namespace details */
> +
> +namespace utils {
> +
> +template<typename C>
> +constexpr auto size(const C &c) -> decltype(c.size())
> +{
> +	return c.size();
> +}
> +
> +template<typename C>
> +constexpr auto data(const C &c) -> decltype(c.data())
> +{
> +	return c.data();
> +}
> +
> +template<typename C>
> +constexpr auto data(C &c) -> decltype(c.data())
> +{
> +	return c.data();
> +}
> +
> +template<class T, std::size_t N>
> +constexpr T *data(T (&array)[N]) noexcept
> +{
> +	return array;
> +}
> +
> +template<std::size_t I, typename T>
> +struct tuple_element;
> +
> +template<std::size_t I, typename T, std::size_t N>
> +struct tuple_element<I, Span<T, N>> {
> +	using type = T;
> +};
> +
> +template<typename T>
> +struct tuple_size;
> +
> +template<typename T, std::size_t N>
> +struct tuple_size<Span<T, N>> : public std::integral_constant<std::size_t, N> {
> +};
> +
> +template<typename T>
> +struct tuple_size<Span<T, dynamic_extent>>;
> +
> +} /* namespace utils */
> +
> +template<typename T, std::size_t Extent>
> +class Span
> +{
> +public:
> +	using element_type = T;
> +	using value_type = typename std::remove_cv_t<T>;
> +	using size_type = std::size_t;
> +	using difference_type = std::ptrdiff_t;
> +	using pointer = T *;
> +	using const_pointer = const T *;
> +	using reference = T &;
> +	using const_reference = const T &;
> +	using iterator = pointer;
> +	using const_iterator = const_pointer;
> +	using reverse_iterator = std::reverse_iterator<iterator>;
> +	using const_reverse_iterator = std::reverse_iterator<const_iterator>;
> +
> +	static constexpr std::size_t extent = Extent;
> +
> +	template<bool Dependent = false,
> +		 typename = std::enable_if_t<Dependent || Extent == 0>>
> +	constexpr Span() noexcept
> +		: data_(nullptr)
> +	{
> +	}
> +
> +	constexpr Span(pointer ptr, size_type count)
> +		: data_(ptr)
> +	{
> +	}
> +
> +	constexpr Span(pointer first, pointer last)
> +		: data_(first)
> +	{
> +	}
> +
> +	template<std::size_t N>
> +	constexpr Span(element_type (&arr)[N],
> +		       std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],
> +							    element_type (*)[]>::value &&
> +					N == Extent,
> +					std::nullptr_t> = nullptr) noexcept
> +		: data_(arr)
> +	{
> +	}
> +
> +	template<std::size_t N>
> +	constexpr Span(std::array<value_type, N> &arr,
> +		       std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],
> +							    element_type (*)[]>::value &&
> +					N == Extent,
> +					std::nullptr_t> = nullptr) noexcept
> +		: data_(arr.data())
> +	{
> +	}
> +
> +	template<std::size_t N>
> +	constexpr Span(const std::array<value_type, N> &arr,
> +		       std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],
> +							    element_type (*)[]>::value &&
> +					N == Extent,
> +					std::nullptr_t> = nullptr) noexcept
> +		: data_(arr.data())
> +	{
> +	}
> +
> +	template<class Container>
> +	constexpr Span(Container &cont,
> +		       std::enable_if_t<!details::is_span<Container>::value &&
> +					!details::is_array<Container>::value &&
> +					!std::is_array<Container>::value &&
> +					std::is_convertible<std::remove_pointer_t<decltype(utils::data(cont))> (*)[],
> +							    element_type (*)[]>::value,
> +					std::nullptr_t> = nullptr)
> +		: data_(utils::data(cont))
> +	{
> +	}
> +
> +	template<class Container>
> +	constexpr Span(const Container &cont,
> +		       std::enable_if_t<!details::is_span<Container>::value &&
> +					!details::is_array<Container>::value &&
> +					!std::is_array<Container>::value &&
> +					std::is_convertible<std::remove_pointer_t<decltype(utils::data(cont))> (*)[],
> +							    element_type (*)[]>::value,
> +					std::nullptr_t> = nullptr)
> +		: data_(utils::data(cont))
> +	{
> +		static_assert(utils::size(cont) == Extent, "Size mismatch");
> +	}
> +
> +	template<class U, std::size_t N>
> +	constexpr Span(const Span<U, N> &s,
> +		       std::enable_if_t<std::is_convertible<U (*)[], element_type (*)[]>::value &&
> +					N == Extent,
> +					std::nullptr_t> = nullptr) noexcept
> +		: data_(s.data())
> +	{
> +	}
> +
> +	constexpr Span(const Span &other) noexcept = default;
> +
> +	constexpr Span &operator=(const Span &other) noexcept
> +	{
> +		data_ = other.data_;
> +		return *this;
> +	}
> +
> +	constexpr iterator begin() const { return data(); }
> +	constexpr const_iterator cbegin() const { return begin(); }
> +	constexpr iterator end() const { return data() + size(); }
> +	constexpr const_iterator cend() const { return end(); }
> +	constexpr reverse_iterator rbegin() const { return reverse_iterator(data() + size() - 1); }
> +	constexpr const_reverse_iterator crbegin() const { return rbegin(); }
> +	constexpr reverse_iterator rend() const { return reverse_iterator(data() - 1); }
> +	constexpr const_reverse_iterator crend() const { return rend(); }
> +
> +	constexpr reference front() const { return *data(); }
> +	constexpr reference back() const { return *(data() + size() - 1); }
> +	constexpr reference operator[](size_type idx) const { return data()[idx]; }
> +	constexpr pointer data() const noexcept { return data_; }
> +
> +	constexpr size_type size() const noexcept { return Extent; }
> +	constexpr size_type size_bytes() const noexcept { return size() * sizeof(element_type); }
> +	constexpr bool empty() const noexcept { return size() == 0; }
> +
> +	template<std::size_t Count>
> +	constexpr Span<element_type, Count> first() const
> +	{
> +		static_assert(Count <= Extent, "Count larger than size");
> +		return { data(), Count };
> +	}
> +
> +	constexpr Span<element_type, dynamic_extent> first(std::size_t Count) const
> +	{
> +		return { data(), Count };
> +	}
> +
> +	template<std::size_t Count>
> +	constexpr Span<element_type, Count> last() const
> +	{
> +		static_assert(Count <= Extent, "Count larger than size");
> +		return { data() + size() - Count, Count };
> +	}
> +
> +	constexpr Span<element_type, dynamic_extent> last(std::size_t Count) const
> +	{
> +		return { data() + size() - Count, Count };
> +	}
> +
> +	template<std::size_t Offset, std::size_t Count = dynamic_extent>
> +	constexpr Span<element_type, Count != dynamic_extent ? Count : Extent - Offset> subspan() const
> +	{
> +		static_assert(Offset <= Extent, "Offset larger than size");
> +		static_assert(Count == dynamic_extent || Count + Offset <= Extent,
> +			      "Offset + Count larger than size");
> +		return { data() + Offset, Count == dynamic_extent ? size() - Offset : Count };
> +	}
> +
> +	constexpr Span<element_type, dynamic_extent>
> +	subspan(std::size_t Offset, std::size_t Count = dynamic_extent) const
> +	{
> +		return { data() + Offset, Count == dynamic_extent ? size() - Offset : Count };
> +	}
> +
> +private:
> +	pointer data_;
> +};
> +
> +template<typename T>
> +class Span<T, dynamic_extent>
> +{
> +public:
> +	using element_type = T;
> +	using value_type = typename std::remove_cv_t<T>;
> +	using size_type = std::size_t;
> +	using difference_type = std::ptrdiff_t;
> +	using pointer = T *;
> +	using const_pointer = const T *;
> +	using reference = T &;
> +	using const_reference = const T &;
> +	using iterator = T *;
> +	using const_iterator = const T *;
> +	using reverse_iterator = std::reverse_iterator<iterator>;
> +	using const_reverse_iterator = std::reverse_iterator<const_iterator>;
> +
> +	static constexpr std::size_t extent = dynamic_extent;
> +
> +	constexpr Span() noexcept
> +		: data_(nullptr), size_(0)
> +	{
> +	}
> +
> +	constexpr Span(pointer ptr, size_type count)
> +		: data_(ptr), size_(count)
> +	{
> +	}
> +
> +	constexpr Span(pointer first, pointer last)
> +		: data_(first), size_(last - first)
> +	{
> +	}
> +
> +	template<std::size_t N>
> +	constexpr Span(element_type (&arr)[N],
> +		       std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],
> +							    element_type (*)[]>::value,
> +					std::nullptr_t> = nullptr) noexcept
> +		: data_(arr), size_(N)
> +	{
> +	}
> +
> +	template<std::size_t N>
> +	constexpr Span(std::array<value_type, N> &arr,
> +		       std::enable_if_t<std::is_convertible<std::remove_pointer_t<decltype(utils::data(arr))> (*)[],
> +							    element_type (*)[]>::value,
> +					std::nullptr_t> = nullptr) noexcept
> +		: data_(utils::data(arr)), size_(N)
> +	{
> +	}
> +
> +	template<std::size_t N>
> +	constexpr Span(const std::array<value_type, N> &arr) noexcept
> +		: data_(utils::data(arr)), size_(N)
> +	{
> +	}
> +
> +	template<class Container>
> +	constexpr Span(Container &cont,
> +		       std::enable_if_t<!details::is_span<Container>::value &&
> +					!details::is_array<Container>::value &&
> +					!std::is_array<Container>::value &&
> +					std::is_convertible<std::remove_pointer_t<decltype(utils::data(cont))> (*)[],
> +							    element_type (*)[]>::value,
> +					std::nullptr_t> = nullptr)
> +		: data_(utils::data(cont)), size_(utils::size(cont))
> +	{
> +	}
> +
> +	template<class Container>
> +	constexpr Span(const Container &cont,
> +		       std::enable_if_t<!details::is_span<Container>::value &&
> +					!details::is_array<Container>::value &&
> +					!std::is_array<Container>::value &&
> +					std::is_convertible<std::remove_pointer_t<decltype(utils::data(cont))> (*)[],
> +							    element_type (*)[]>::value,
> +					std::nullptr_t> = nullptr)
> +		: data_(utils::data(cont)), size_(utils::size(cont))
> +	{
> +	}
> +
> +	template<class U, std::size_t N>
> +	constexpr Span(const Span<U, N> &s,
> +		       std::enable_if_t<std::is_convertible<U (*)[], element_type (*)[]>::value,
> +					std::nullptr_t> = nullptr) noexcept
> +		: data_(s.data()), size_(s.size())
> +	{
> +	}
> +
> +	constexpr Span(const Span &other) noexcept = default;
> +
> +	constexpr Span &operator=(const Span &other) noexcept
> +	{
> +		data_ = other.data_;
> +		size_ = other.size_;
> +		return *this;
> +	}
> +
> +	constexpr iterator begin() const { return data(); }
> +	constexpr const_iterator cbegin() const { return begin(); }
> +	constexpr iterator end() const { return data() + size(); }
> +	constexpr const_iterator cend() const { return end(); }
> +	constexpr reverse_iterator rbegin() const { return reverse_iterator(data() + size() - 1); }
> +	constexpr const_reverse_iterator crbegin() const { return rbegin(); }
> +	constexpr reverse_iterator rend() const { return reverse_iterator(data() - 1); }
> +	constexpr const_reverse_iterator crend() const { return rend(); }
> +
> +	constexpr reference front() const { return *data(); }
> +	constexpr reference back() const { return *(data() + size() - 1); }
> +	constexpr reference operator[](size_type idx) const { return data()[idx]; }
> +	constexpr pointer data() const noexcept { return data_; }
> +
> +	constexpr size_type size() const noexcept { return size_; }
> +	constexpr size_type size_bytes() const noexcept { return size() * sizeof(element_type); }
> +	constexpr bool empty() const noexcept { return size() == 0; }
> +
> +	template<std::size_t Count>
> +	constexpr Span<element_type, Count> first() const
> +	{
> +		return { data(), Count };
> +	}
> +
> +	constexpr Span<element_type, dynamic_extent> first(std::size_t Count) const
> +	{
> +		return { data(), Count };
> +	}
> +
> +	template<std::size_t Count>
> +	constexpr Span<element_type, Count> last() const
> +	{
> +		return { data() + size() - Count, Count };
> +	}
> +
> +	constexpr Span<element_type, dynamic_extent> last(std::size_t Count) const
> +	{
> +		return { data() + size() - Count, Count };
> +	}
> +
> +	template<std::size_t Offset, std::size_t Count = dynamic_extent>
> +	constexpr Span<element_type, Count> subspan() const
> +	{
> +		return { data() + Offset, Count == dynamic_extent ? size() - Offset : Count };
> +	}
> +
> +	constexpr Span<element_type, dynamic_extent>
> +	subspan(std::size_t Offset, std::size_t Count = dynamic_extent) const
> +	{
> +		return { data() + Offset, Count == dynamic_extent ? size() - Offset : Count };
> +	}
> +
> +private:
> +	pointer data_;
> +	size_type size_;
> +};
> +
> +}; /* namespace libcamera */
> +
> +#endif /* __LIBCAMERA_SPAN_H__ */
> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> index 88658ac563f7..2448f0e96468 100644
> --- a/src/libcamera/meson.build
> +++ b/src/libcamera/meson.build
> @@ -36,6 +36,7 @@ libcamera_sources = files([
>      'request.cpp',
>      'semaphore.cpp',
>      'signal.cpp',
> +    'span.cpp',
>      'stream.cpp',
>      'thread.cpp',
>      'timer.cpp',
> diff --git a/src/libcamera/span.cpp b/src/libcamera/span.cpp
> new file mode 100644
> index 000000000000..753104765cea
> --- /dev/null
> +++ b/src/libcamera/span.cpp
> @@ -0,0 +1,12 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2020, Google Inc.
> + *
> + * span.h - C++20 std::span<> implementation for C++11

.h? But if this file is dropped then it goes away.


> + */
> +
> +#include <libcamera/span.h>
> +
> +namespace libcamera {
> +
> +} /* namespace libcamera */
> 


Do we actually need span.cpp at all?

-- 
Regards
--
Kieran


More information about the libcamera-devel mailing list