A C++ toolbox repo until the pair uTL/dTL arives
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

608 lines
18 KiB

  1. /*
  2. * This is an implementation of C++20's std::span
  3. * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/n4820.pdf
  4. */
  5. // Copyright Tristan Brindle 2018.
  6. // Distributed under the Boost Software License, Version 1.0.
  7. // https://www.boost.org/LICENSE_1_0.txt
  8. #ifndef TBX_CONT_SPAN_H_
  9. #define TBX_CONT_SPAN_H_
  10. #include <array>
  11. #include <cstddef>
  12. #include <cstdint>
  13. #include <type_traits>
  14. #ifndef TBX_SPAN_NO_EXCEPTIONS
  15. // Attempt to discover whether we're being compiled with exception support
  16. #if !(defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND))
  17. #define TBX_SPAN_NO_EXCEPTIONS
  18. #endif
  19. #endif
  20. #ifndef TBX_SPAN_NO_EXCEPTIONS
  21. #include <cstdio>
  22. #include <stdexcept>
  23. #endif
  24. // Various feature test macros
  25. #if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
  26. #define TBX_SPAN_HAVE_CPP17
  27. #endif
  28. #if __cplusplus >= 201402L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L)
  29. #define TBX_SPAN_HAVE_CPP14
  30. #endif
  31. namespace tbx {
  32. // Establish default contract checking behavior
  33. #if !defined(TBX_SPAN_THROW_ON_CONTRACT_VIOLATION) && \
  34. !defined(TBX_SPAN_TERMINATE_ON_CONTRACT_VIOLATION) && \
  35. !defined(TBX_SPAN_NO_CONTRACT_CHECKING)
  36. #if defined(NDEBUG) || !defined(TBX_SPAN_HAVE_CPP14)
  37. #define TBX_SPAN_NO_CONTRACT_CHECKING
  38. #else
  39. #define TBX_SPAN_TERMINATE_ON_CONTRACT_VIOLATION
  40. #endif
  41. #endif
  42. #if defined(TBX_SPAN_THROW_ON_CONTRACT_VIOLATION)
  43. struct contract_violation_error : std::logic_error {
  44. explicit contract_violation_error(const char* msg) : std::logic_error(msg)
  45. {}
  46. };
  47. inline void contract_violation(const char* msg)
  48. {
  49. throw contract_violation_error(msg);
  50. }
  51. #elif defined(TBX_SPAN_TERMINATE_ON_CONTRACT_VIOLATION)
  52. [[noreturn]] inline void contract_violation(const char* /*unused*/)
  53. {
  54. std::terminate();
  55. }
  56. #endif
  57. #if !defined(TBX_SPAN_NO_CONTRACT_CHECKING)
  58. #define TBX_SPAN_STRINGIFY(cond) #cond
  59. #define TBX_SPAN_EXPECT(cond) \
  60. cond ? (void) 0 : contract_violation("Expected " TBX_SPAN_STRINGIFY(cond))
  61. #else
  62. #define TBX_SPAN_EXPECT(cond)
  63. #endif
  64. #if defined(TBX_SPAN_HAVE_CPP17) || defined(__cpp_inline_variables)
  65. #define TBX_SPAN_INLINE_VAR inline
  66. #else
  67. #define TBX_SPAN_INLINE_VAR
  68. #endif
  69. #if defined(TBX_SPAN_HAVE_CPP14) || \
  70. (defined(__cpp_constexpr) && __cpp_constexpr >= 201304)
  71. #define TBX_SPAN_HAVE_CPP14_CONSTEXPR
  72. #endif
  73. #if defined(TBX_SPAN_HAVE_CPP14_CONSTEXPR)
  74. #define TBX_SPAN_CONSTEXPR14 constexpr
  75. #else
  76. #define TBX_SPAN_CONSTEXPR14
  77. #endif
  78. #if defined(TBX_SPAN_HAVE_CPP14_CONSTEXPR) && \
  79. (!defined(_MSC_VER) || _MSC_VER > 1900)
  80. #define TBX_SPAN_CONSTEXPR_ASSIGN constexpr
  81. #else
  82. #define TBX_SPAN_CONSTEXPR_ASSIGN
  83. #endif
  84. #if defined(TBX_SPAN_NO_CONTRACT_CHECKING)
  85. #define TBX_SPAN_CONSTEXPR11 constexpr
  86. #else
  87. #define TBX_SPAN_CONSTEXPR11 TBX_SPAN_CONSTEXPR14
  88. #endif
  89. #if defined(TBX_SPAN_HAVE_CPP17) || defined(__cpp_deduction_guides)
  90. #define TBX_SPAN_HAVE_DEDUCTION_GUIDES
  91. #endif
  92. #if defined(TBX_SPAN_HAVE_CPP17) || defined(__cpp_lib_byte)
  93. #define TBX_SPAN_HAVE_STD_BYTE
  94. #endif
  95. #if defined(TBX_SPAN_HAVE_CPP17) || defined(__cpp_lib_array_constexpr)
  96. #define TBX_SPAN_HAVE_CONSTEXPR_STD_ARRAY_ETC
  97. #endif
  98. #if defined(TBX_SPAN_HAVE_CONSTEXPR_STD_ARRAY_ETC)
  99. #define TBX_SPAN_ARRAY_CONSTEXPR constexpr
  100. #else
  101. #define TBX_SPAN_ARRAY_CONSTEXPR
  102. #endif
  103. #ifdef TBX_SPAN_HAVE_STD_BYTE
  104. using byte = std::byte;
  105. #else
  106. using byte = unsigned char;
  107. #endif
  108. #if defined(TBX_SPAN_HAVE_CPP17)
  109. #define TBX_SPAN_NODISCARD [[nodiscard]]
  110. #else
  111. #define TBX_SPAN_NODISCARD
  112. #endif
  113. TBX_SPAN_INLINE_VAR constexpr std::size_t dynamic_extent = SIZE_MAX;
  114. template <typename ElementType, std::size_t Extent = dynamic_extent>
  115. class span;
  116. namespace detail {
  117. template <typename E, std::size_t S>
  118. struct span_storage {
  119. constexpr span_storage() noexcept = default;
  120. constexpr span_storage(E* p_ptr, std::size_t /*unused*/) noexcept
  121. : ptr(p_ptr)
  122. {}
  123. E* ptr = nullptr;
  124. static constexpr std::size_t size = S;
  125. };
  126. template <typename E>
  127. struct span_storage<E, dynamic_extent> {
  128. constexpr span_storage() noexcept = default;
  129. constexpr span_storage(E* p_ptr, std::size_t p_size) noexcept
  130. : ptr(p_ptr), size(p_size)
  131. {}
  132. E* ptr = nullptr;
  133. std::size_t size = 0;
  134. };
  135. // Reimplementation of C++17 std::size() and std::data()
  136. #if defined(TBX_SPAN_HAVE_CPP17) || \
  137. defined(__cpp_lib_nonmember_container_access)
  138. using std::data;
  139. using std::size;
  140. #else
  141. template <class C>
  142. constexpr auto size(const C& c) -> decltype(c.size())
  143. {
  144. return c.size();
  145. }
  146. template <class T, std::size_t N>
  147. constexpr std::size_t size(const T (&)[N]) noexcept
  148. {
  149. return N;
  150. }
  151. template <class C>
  152. constexpr auto data(C& c) -> decltype(c.data())
  153. {
  154. return c.data();
  155. }
  156. template <class C>
  157. constexpr auto data(const C& c) -> decltype(c.data())
  158. {
  159. return c.data();
  160. }
  161. template <class T, std::size_t N>
  162. constexpr T* data(T (&array)[N]) noexcept
  163. {
  164. return array;
  165. }
  166. template <class E>
  167. constexpr const E* data(std::initializer_list<E> il) noexcept
  168. {
  169. return il.begin();
  170. }
  171. #endif // TBX_SPAN_HAVE_CPP17
  172. #if defined(TBX_SPAN_HAVE_CPP17) || defined(__cpp_lib_void_t)
  173. using std::void_t;
  174. #else
  175. template <typename...>
  176. using void_t = void;
  177. #endif
  178. template <typename T>
  179. using uncvref_t =
  180. typename std::remove_cv<typename std::remove_reference<T>::type>::type;
  181. template <typename>
  182. struct is_span : std::false_type {};
  183. template <typename T, std::size_t S>
  184. struct is_span<span<T, S>> : std::true_type {};
  185. template <typename>
  186. struct is_std_array : std::false_type {};
  187. template <typename T, std::size_t N>
  188. struct is_std_array<std::array<T, N>> : std::true_type {};
  189. template <typename, typename = void>
  190. struct has_size_and_data : std::false_type {};
  191. template <typename T>
  192. struct has_size_and_data<T, void_t<decltype(detail::size(std::declval<T>())),
  193. decltype(detail::data(std::declval<T>()))>>
  194. : std::true_type {};
  195. template <typename C, typename U = uncvref_t<C>>
  196. struct is_container {
  197. static constexpr bool value =
  198. !is_span<U>::value && !is_std_array<U>::value &&
  199. !std::is_array<U>::value && has_size_and_data<C>::value;
  200. };
  201. template <typename T>
  202. using remove_pointer_t = typename std::remove_pointer<T>::type;
  203. template <typename, typename, typename = void>
  204. struct is_container_element_type_compatible : std::false_type {};
  205. template <typename T, typename E>
  206. struct is_container_element_type_compatible<
  207. T, E,
  208. typename std::enable_if<
  209. !std::is_same<typename std::remove_cv<decltype(
  210. detail::data(std::declval<T>()))>::type,
  211. void>::value>::type>
  212. : std::is_convertible<
  213. remove_pointer_t<decltype(detail::data(std::declval<T>()))> (*)[],
  214. E (*)[]> {};
  215. template <typename, typename = size_t>
  216. struct is_complete : std::false_type {};
  217. template <typename T>
  218. struct is_complete<T, decltype(sizeof(T))> : std::true_type {};
  219. } // namespace detail
  220. template <typename ElementType, std::size_t Extent>
  221. class span {
  222. static_assert(std::is_object<ElementType>::value,
  223. "A span's ElementType must be an object type (not a "
  224. "reference type or void)");
  225. static_assert(detail::is_complete<ElementType>::value,
  226. "A span's ElementType must be a complete type (not a forward "
  227. "declaration)");
  228. static_assert(!std::is_abstract<ElementType>::value,
  229. "A span's ElementType cannot be an abstract class type");
  230. using storage_type = detail::span_storage<ElementType, Extent>;
  231. public:
  232. // constants and types
  233. using element_type = ElementType;
  234. using value_type = typename std::remove_cv<ElementType>::type;
  235. using size_type = std::size_t;
  236. using difference_type = std::ptrdiff_t;
  237. using pointer = element_type*;
  238. using const_pointer = const element_type*;
  239. using reference = element_type&;
  240. using const_reference = const element_type&;
  241. using iterator = pointer;
  242. using reverse_iterator = std::reverse_iterator<iterator>;
  243. static constexpr size_type extent = Extent;
  244. // [span.cons], span constructors, copy, assignment, and destructor
  245. template <
  246. std::size_t E = Extent,
  247. typename std::enable_if<(E == dynamic_extent || E <= 0), int>::type = 0>
  248. constexpr span() noexcept
  249. {}
  250. TBX_SPAN_CONSTEXPR11 span(pointer ptr, size_type count)
  251. : storage_(ptr, count)
  252. {
  253. TBX_SPAN_EXPECT(extent == dynamic_extent || count == extent);
  254. }
  255. TBX_SPAN_CONSTEXPR11 span(pointer first_elem, pointer last_elem)
  256. : storage_(first_elem, last_elem - first_elem)
  257. {
  258. TBX_SPAN_EXPECT(extent == dynamic_extent ||
  259. last_elem - first_elem ==
  260. static_cast<std::ptrdiff_t>(extent));
  261. }
  262. template <std::size_t N, std::size_t E = Extent,
  263. typename std::enable_if<
  264. (E == dynamic_extent || N == E) &&
  265. detail::is_container_element_type_compatible<
  266. element_type (&)[N], ElementType>::value,
  267. int>::type = 0>
  268. constexpr span(element_type (&arr)[N]) noexcept : storage_(arr, N)
  269. {}
  270. template <std::size_t N, std::size_t E = Extent,
  271. typename std::enable_if<
  272. (E == dynamic_extent || N == E) &&
  273. detail::is_container_element_type_compatible<
  274. std::array<value_type, N>&, ElementType>::value,
  275. int>::type = 0>
  276. TBX_SPAN_ARRAY_CONSTEXPR span(std::array<value_type, N>& arr) noexcept
  277. : storage_(arr.data(), N)
  278. {}
  279. template <std::size_t N, std::size_t E = Extent,
  280. typename std::enable_if<
  281. (E == dynamic_extent || N == E) &&
  282. detail::is_container_element_type_compatible<
  283. const std::array<value_type, N>&, ElementType>::value,
  284. int>::type = 0>
  285. TBX_SPAN_ARRAY_CONSTEXPR span(const std::array<value_type, N>& arr) noexcept
  286. : storage_(arr.data(), N)
  287. {}
  288. template <
  289. typename Container, std::size_t E = Extent,
  290. typename std::enable_if<
  291. E == dynamic_extent && detail::is_container<Container>::value &&
  292. detail::is_container_element_type_compatible<
  293. Container&, ElementType>::value,
  294. int>::type = 0>
  295. constexpr span(Container& cont)
  296. // : storage_(detail::data(cont), detail::size(cont))
  297. : storage_(cont.data(), cont.size())
  298. {}
  299. template <
  300. typename Container, std::size_t E = Extent,
  301. typename std::enable_if<
  302. E == dynamic_extent && detail::is_container<Container>::value &&
  303. detail::is_container_element_type_compatible<
  304. const Container&, ElementType>::value,
  305. int>::type = 0>
  306. constexpr span(const Container& cont)
  307. : storage_(detail::data(cont), detail::size(cont))
  308. {}
  309. constexpr span(const span& other) noexcept = default;
  310. template <typename OtherElementType, std::size_t OtherExtent,
  311. typename std::enable_if<
  312. (Extent == OtherExtent || Extent == dynamic_extent) &&
  313. std::is_convertible<OtherElementType (*)[],
  314. ElementType (*)[]>::value,
  315. int>::type = 0>
  316. constexpr span(const span<OtherElementType, OtherExtent>& other) noexcept
  317. : storage_(other.data(), other.size())
  318. {}
  319. ~span() noexcept = default;
  320. TBX_SPAN_CONSTEXPR_ASSIGN span&
  321. operator=(const span& other) noexcept = default;
  322. // [span.sub], span subviews
  323. template <std::size_t Count>
  324. TBX_SPAN_CONSTEXPR11 span<element_type, Count> first() const
  325. {
  326. TBX_SPAN_EXPECT(Count <= size());
  327. return {data(), Count};
  328. }
  329. template <std::size_t Count>
  330. TBX_SPAN_CONSTEXPR11 span<element_type, Count> last() const
  331. {
  332. TBX_SPAN_EXPECT(Count <= size());
  333. return {data() + (size() - Count), Count};
  334. }
  335. template <std::size_t Offset, std::size_t Count = dynamic_extent>
  336. using subspan_return_t =
  337. span<ElementType, Count != dynamic_extent
  338. ? Count
  339. : (Extent != dynamic_extent ? Extent - Offset
  340. : dynamic_extent)>;
  341. template <std::size_t Offset, std::size_t Count = dynamic_extent>
  342. TBX_SPAN_CONSTEXPR11 subspan_return_t<Offset, Count> subspan() const
  343. {
  344. TBX_SPAN_EXPECT(Offset <= size() &&
  345. (Count == dynamic_extent || Offset + Count <= size()));
  346. return {data() + Offset,
  347. Count != dynamic_extent ? Count : size() - Offset};
  348. }
  349. TBX_SPAN_CONSTEXPR11 span<element_type, dynamic_extent>
  350. first(size_type count) const
  351. {
  352. TBX_SPAN_EXPECT(count <= size());
  353. return {data(), count};
  354. }
  355. TBX_SPAN_CONSTEXPR11 span<element_type, dynamic_extent>
  356. last(size_type count) const
  357. {
  358. TBX_SPAN_EXPECT(count <= size());
  359. return {data() + (size() - count), count};
  360. }
  361. TBX_SPAN_CONSTEXPR11 span<element_type, dynamic_extent>
  362. subspan(size_type offset, size_type count = dynamic_extent) const
  363. {
  364. TBX_SPAN_EXPECT(offset <= size() &&
  365. (count == dynamic_extent || offset + count <= size()));
  366. return {data() + offset,
  367. count == dynamic_extent ? size() - offset : count};
  368. }
  369. // [span.obs], span observers
  370. constexpr size_type size() const noexcept { return storage_.size; }
  371. constexpr size_type size_bytes() const noexcept
  372. {
  373. return size() * sizeof(element_type);
  374. }
  375. TBX_SPAN_NODISCARD constexpr bool empty() const noexcept
  376. {
  377. return size() == 0;
  378. }
  379. // [span.elem], span element access
  380. TBX_SPAN_CONSTEXPR11 reference operator[](size_type idx) const
  381. {
  382. TBX_SPAN_EXPECT(idx < size());
  383. return *(data() + idx);
  384. }
  385. TBX_SPAN_CONSTEXPR11 reference front() const
  386. {
  387. TBX_SPAN_EXPECT(!empty());
  388. return *data();
  389. }
  390. TBX_SPAN_CONSTEXPR11 reference back() const
  391. {
  392. TBX_SPAN_EXPECT(!empty());
  393. return *(data() + (size() - 1));
  394. }
  395. constexpr pointer data() const noexcept { return storage_.ptr; }
  396. // [span.iterators], span iterator support
  397. constexpr iterator begin() const noexcept { return data(); }
  398. constexpr iterator end() const noexcept { return data() + size(); }
  399. TBX_SPAN_ARRAY_CONSTEXPR reverse_iterator rbegin() const noexcept
  400. {
  401. return reverse_iterator(end());
  402. }
  403. TBX_SPAN_ARRAY_CONSTEXPR reverse_iterator rend() const noexcept
  404. {
  405. return reverse_iterator(begin());
  406. }
  407. private:
  408. storage_type storage_{};
  409. };
  410. #ifdef TBX_SPAN_HAVE_DEDUCTION_GUIDES
  411. /* Deduction Guides */
  412. template <class T, size_t N>
  413. span(T (&)[N])->span<T, N>;
  414. template <class T, size_t N>
  415. span(std::array<T, N>&)->span<T, N>;
  416. template <class T, size_t N>
  417. span(const std::array<T, N>&)->span<const T, N>;
  418. template <class Container>
  419. span(Container&)->span<typename Container::value_type>;
  420. template <class Container>
  421. span(const Container&)->span<const typename Container::value_type>;
  422. #endif // TCB_HAVE_DEDUCTION_GUIDES
  423. template <typename ElementType, std::size_t Extent>
  424. constexpr span<ElementType, Extent>
  425. make_span(span<ElementType, Extent> s) noexcept
  426. {
  427. return s;
  428. }
  429. template <typename T, std::size_t N>
  430. constexpr span<T, N> make_span(T (&arr)[N]) noexcept
  431. {
  432. return {arr};
  433. }
  434. template <typename T, std::size_t N>
  435. TBX_SPAN_ARRAY_CONSTEXPR span<T, N> make_span(std::array<T, N>& arr) noexcept
  436. {
  437. return {arr};
  438. }
  439. template <typename T, std::size_t N>
  440. TBX_SPAN_ARRAY_CONSTEXPR span<const T, N>
  441. make_span(const std::array<T, N>& arr) noexcept
  442. {
  443. return {arr};
  444. }
  445. template <typename Container>
  446. constexpr span<typename Container::value_type> make_span(Container& cont)
  447. {
  448. return {cont};
  449. }
  450. template <typename Container>
  451. constexpr span<const typename Container::value_type>
  452. make_span(const Container& cont)
  453. {
  454. return {cont};
  455. }
  456. template <typename ElementType, std::size_t Extent>
  457. span<const byte, ((Extent == dynamic_extent) ? dynamic_extent
  458. : sizeof(ElementType) * Extent)>
  459. as_bytes(span<ElementType, Extent> s) noexcept
  460. {
  461. return {reinterpret_cast<const byte*>(s.data()), s.size_bytes()};
  462. }
  463. template <
  464. class ElementType, size_t Extent,
  465. typename std::enable_if<!std::is_const<ElementType>::value, int>::type = 0>
  466. span<byte, ((Extent == dynamic_extent) ? dynamic_extent
  467. : sizeof(ElementType) * Extent)>
  468. as_writable_bytes(span<ElementType, Extent> s) noexcept
  469. {
  470. return {reinterpret_cast<byte*>(s.data()), s.size_bytes()};
  471. }
  472. template <std::size_t N, typename E, std::size_t S>
  473. constexpr auto get(span<E, S> s) -> decltype(s[N])
  474. {
  475. return s[N];
  476. }
  477. } // namespace tbx
  478. namespace std {
  479. template <typename ElementType, size_t Extent>
  480. class tuple_size<tbx::span<ElementType, Extent>>
  481. : public integral_constant<size_t, Extent> {};
  482. template <typename ElementType>
  483. class tuple_size<tbx::span<
  484. ElementType, tbx::dynamic_extent>>; // not defined
  485. template <size_t I, typename ElementType, size_t Extent>
  486. class tuple_element<I, tbx::span<ElementType, Extent>> {
  487. public:
  488. static_assert(Extent != tbx::dynamic_extent &&
  489. I < Extent,
  490. "");
  491. using type = ElementType;
  492. };
  493. } // end namespace std
  494. #endif // TBX_CONT_SPAN_H_