diff --git a/src/core/span_type.hpp b/src/core/span_type.hpp new file mode 100644 --- /dev/null +++ b/src/core/span_type.hpp @@ -0,0 +1,98 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file span_type.hpp Minimized implementation of C++20 std::span. */ + +#ifndef CORE_SPAN_TYPE_HPP +#define CORE_SPAN_TYPE_HPP + +/* This is a partial copy/paste from https://github.com/gsl-lite/gsl-lite/blob/master/include/gsl/gsl-lite.hpp */ + +/* Template to check if a template variable defines size() and data(). */ +template +struct has_size_and_data : std::false_type{}; +template +struct has_size_and_data +< + C, std::void_t< + decltype(std::size(std::declval())), + decltype(std::data(std::declval()))> +> : std::true_type{}; + +/* Template to check if two elements are compatible. */ +template +struct is_compatible_element : std::false_type {}; +template +struct is_compatible_element +< + C, E, std::void_t< + decltype(std::data(std::declval())), + typename std::remove_pointer()))>::type(*)[]> +> : std::is_convertible()))>::type(*)[], E(*)[]>{}; + +/* Template to check if a container is compatible. gsl-lite also includes is_array and is_std_array, but as we don't use them, they are omitted. */ +template +struct is_compatible_container : std::bool_constant +< + has_size_and_data::value + && is_compatible_element::value +>{}; + +/** + * A trimmed down version of what std::span will be in C++20. + * + * It is fully forwards compatible, so if this codebase switches to C++20, + * all "span" instances can be replaced by "std::span" without loss of + * functionality. + * + * Currently it only supports basic functionality: + * - size() and friends + * - begin() and friends + * + * It is meant to simplify function parameters, where we only want to walk + * a continuous list. + */ +template +class span { +public: + typedef T element_type; + typedef typename std::remove_cv< T >::type value_type; + + typedef T &reference; + typedef T *pointer; + typedef const T &const_reference; + typedef const T *const_pointer; + + typedef pointer iterator; + typedef const_pointer const_iterator; + + typedef size_t size_type; + typedef std::ptrdiff_t difference_type; + + constexpr span(pointer data_in, size_t size_in) : first(data_in), last(data_in + size_in) {} + + template::value), int>::type = 0> + constexpr span(Container &list) noexcept : first(std::data(list)), last(std::data(list) + std::size(list)) {} + template::value && is_compatible_container::value), int>::type = 0> + constexpr span(const Container &list) noexcept : first(std::data(list)), last(std::data(list) + std::size(list)) {} + + constexpr size_t size() const noexcept { return static_cast( last - first ); } + constexpr std::ptrdiff_t ssize() const noexcept { return static_cast( last - first ); } + constexpr bool empty() const noexcept { return size() == 0; } + + constexpr iterator begin() const noexcept { return iterator(first); } + constexpr iterator end() const noexcept { return iterator(last); } + + constexpr const_iterator cbegin() const noexcept { return const_iterator(first); } + constexpr const_iterator cend() const noexcept { return const_iterator(last); } + +private: + pointer first; + pointer last; +}; + +#endif /* CORE_SPAN_TYPE_HPP */