g2o
Loading...
Searching...
No Matches
fixed_array.h
Go to the documentation of this file.
1// Copyright 2018 The Abseil Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// -----------------------------------------------------------------------------
16// File: fixed_array.h
17// -----------------------------------------------------------------------------
18//
19// A `FixedArray<T>` represents a non-resizable array of `T` where the length of
20// the array can be determined at run-time. It is a good replacement for
21// non-standard and deprecated uses of `alloca()` and variable length arrays
22// within the GCC extension. (See
23// https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html).
24//
25// `FixedArray` allocates small arrays inline, keeping performance fast by
26// avoiding heap operations. It also helps reduce the chances of
27// accidentally overflowing your stack if large input is passed to
28// your function.
29
30#ifndef G2O_CERES_PUBLIC_INTERNAL_FIXED_ARRAY_H_
31#define G2O_CERES_PUBLIC_INTERNAL_FIXED_ARRAY_H_
32
33#include <Eigen/Core> // For Eigen::aligned_allocator
34#include <algorithm>
35#include <array>
36#include <cstddef>
37#include <memory>
38#include <tuple>
39#include <type_traits>
40
41#include "memory.h"
42
43namespace g2o {
44namespace ceres {
45namespace internal {
46
47constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
48
49// The default fixed array allocator.
50//
51// As one can not easily detect if a struct contains or inherits from a fixed
52// size Eigen type, to be safe the Eigen::aligned_allocator is used by default.
53// But trivial types can never contain Eigen types, so std::allocator is used to
54// safe some heap memory.
55template <typename T>
57 typename std::conditional<std::is_trivial<T>::value, std::allocator<T>,
58 Eigen::aligned_allocator<T>>::type;
59
60// -----------------------------------------------------------------------------
61// FixedArray
62// -----------------------------------------------------------------------------
63//
64// A `FixedArray` provides a run-time fixed-size array, allocating a small array
65// inline for efficiency.
66//
67// Most users should not specify an `inline_elements` argument and let
68// `FixedArray` automatically determine the number of elements
69// to store inline based on `sizeof(T)`. If `inline_elements` is specified, the
70// `FixedArray` implementation will use inline storage for arrays with a
71// length <= `inline_elements`.
72//
73// Note that a `FixedArray` constructed with a `size_type` argument will
74// default-initialize its values by leaving trivially constructible types
75// uninitialized (e.g. int, int[4], double), and others default-constructed.
76// This matches the behavior of c-style arrays and `std::array`, but not
77// `std::vector`.
78//
79// Note that `FixedArray` does not provide a public allocator; if it requires a
80// heap allocation, it will do so with global `::operator new[]()` and
81// `::operator delete[]()`, even if T provides class-scope overrides for these
82// operators.
83template <typename T, size_t N = kFixedArrayUseDefault,
86 static_assert(!std::is_array<T>::value || std::extent<T>::value > 0,
87 "Arrays with unknown bounds cannot be used with FixedArray.");
88
89 static constexpr size_t kInlineBytesDefault = 256;
90
91 using AllocatorTraits = std::allocator_traits<A>;
92 // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17,
93 // but this seems to be mostly pedantic.
94 template <typename Iterator>
95 using EnableIfForwardIterator = typename std::enable_if<std::is_convertible<
96 typename std::iterator_traits<Iterator>::iterator_category,
97 std::forward_iterator_tag>::value>::type;
98 static constexpr bool DefaultConstructorIsNonTrivial() {
99 return !std::is_trivially_default_constructible<StorageElement>::value;
100 }
101
102 public:
103 using allocator_type = typename AllocatorTraits::allocator_type;
104 using value_type = typename AllocatorTraits::value_type;
105 using pointer = typename AllocatorTraits::pointer;
106 using const_pointer = typename AllocatorTraits::const_pointer;
109 using size_type = typename AllocatorTraits::size_type;
110 using difference_type = typename AllocatorTraits::difference_type;
113 using reverse_iterator = std::reverse_iterator<iterator>;
114 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
115
116 static constexpr size_type inline_elements =
118 : static_cast<size_type>(N));
119
120 FixedArray(const FixedArray& other,
121 const allocator_type& a = allocator_type())
122 : FixedArray(other.begin(), other.end(), a) {}
123
125 const allocator_type& a = allocator_type()) noexcept
126 : FixedArray(std::make_move_iterator(other.begin()),
127 std::make_move_iterator(other.end()), a) {}
128
129 // Creates an array object that can store `n` elements.
130 // Note that trivially constructible elements will be uninitialized.
137
138 // Creates an array initialized with `n` copies of `val`.
140 const allocator_type& a = allocator_type())
141 : storage_(n, a) {
143 }
144
145 // Creates an array initialized with the size and contents of `init_list`.
146 FixedArray(std::initializer_list<value_type> init_list,
147 const allocator_type& a = allocator_type())
148 : FixedArray(init_list.begin(), init_list.end(), a) {}
149
150 // Creates an array initialized with the elements from the input
151 // range. The array's size will always be `std::distance(first, last)`.
152 // REQUIRES: Iterator must be a forward_iterator or better.
153 template <typename Iterator, EnableIfForwardIterator<Iterator>* = nullptr>
154 FixedArray(Iterator first, Iterator last,
155 const allocator_type& a = allocator_type())
156 : storage_(std::distance(first, last), a) {
157 CopyRange(storage_.alloc(), storage_.begin(), first, last);
158 }
159
160 ~FixedArray() noexcept {
161 for (auto* cur = storage_.begin(); cur != storage_.end(); ++cur) {
162 AllocatorTraits::destroy(storage_.alloc(), cur);
163 }
164 }
165
166 // Assignments are deleted because they break the invariant that the size of a
167 // `FixedArray` never changes.
168 void operator=(FixedArray&&) = delete;
169 void operator=(const FixedArray&) = delete;
170
171 // FixedArray::size()
172 //
173 // Returns the length of the fixed array.
174 size_type size() const { return storage_.size(); }
175
176 // FixedArray::max_size()
177 //
178 // Returns the largest possible value of `std::distance(begin(), end())` for a
179 // `FixedArray<T>`. This is equivalent to the most possible addressable bytes
180 // over the number of bytes taken by T.
181 constexpr size_type max_size() const {
182 return (std::numeric_limits<difference_type>::max)() / sizeof(value_type);
183 }
184
185 // FixedArray::empty()
186 //
187 // Returns whether or not the fixed array is empty.
188 bool empty() const { return size() == 0; }
189
190 // FixedArray::memsize()
191 //
192 // Returns the memory size of the fixed array in bytes.
193 size_t memsize() const { return size() * sizeof(value_type); }
194
195 // FixedArray::data()
196 //
197 // Returns a const T* pointer to elements of the `FixedArray`. This pointer
198 // can be used to access (but not modify) the contained elements.
200
201 // Overload of FixedArray::data() to return a T* pointer to elements of the
202 // fixed array. This pointer can be used to access and modify the contained
203 // elements.
205
206 // FixedArray::operator[]
207 //
208 // Returns a reference the ith element of the fixed array.
209 // REQUIRES: 0 <= i < size()
210 reference operator[](size_type i) { return data()[i]; }
211
212 // Overload of FixedArray::operator()[] to return a const reference to the
213 // ith element of the fixed array.
214 // REQUIRES: 0 <= i < size()
215 const_reference operator[](size_type i) const { return data()[i]; }
216
217 // FixedArray::front()
218 //
219 // Returns a reference to the first element of the fixed array.
220 reference front() { return *begin(); }
221
222 // Overload of FixedArray::front() to return a reference to the first element
223 // of a fixed array of const values.
224 const_reference front() const { return *begin(); }
225
226 // FixedArray::back()
227 //
228 // Returns a reference to the last element of the fixed array.
229 reference back() { return *(end() - 1); }
230
231 // Overload of FixedArray::back() to return a reference to the last element
232 // of a fixed array of const values.
233 const_reference back() const { return *(end() - 1); }
234
235 // FixedArray::begin()
236 //
237 // Returns an iterator to the beginning of the fixed array.
238 iterator begin() { return data(); }
239
240 // Overload of FixedArray::begin() to return a const iterator to the
241 // beginning of the fixed array.
242 const_iterator begin() const { return data(); }
243
244 // FixedArray::cbegin()
245 //
246 // Returns a const iterator to the beginning of the fixed array.
247 const_iterator cbegin() const { return begin(); }
248
249 // FixedArray::end()
250 //
251 // Returns an iterator to the end of the fixed array.
252 iterator end() { return data() + size(); }
253
254 // Overload of FixedArray::end() to return a const iterator to the end of the
255 // fixed array.
256 const_iterator end() const { return data() + size(); }
257
258 // FixedArray::cend()
259 //
260 // Returns a const iterator to the end of the fixed array.
261 const_iterator cend() const { return end(); }
262
263 // FixedArray::rbegin()
264 //
265 // Returns a reverse iterator from the end of the fixed array.
267
268 // Overload of FixedArray::rbegin() to return a const reverse iterator from
269 // the end of the fixed array.
273
274 // FixedArray::crbegin()
275 //
276 // Returns a const reverse iterator from the end of the fixed array.
278
279 // FixedArray::rend()
280 //
281 // Returns a reverse iterator from the beginning of the fixed array.
283
284 // Overload of FixedArray::rend() for returning a const reverse iterator
285 // from the beginning of the fixed array.
289
290 // FixedArray::crend()
291 //
292 // Returns a reverse iterator from the beginning of the fixed array.
293 const_reverse_iterator crend() const { return rend(); }
294
295 // FixedArray::fill()
296 //
297 // Assigns the given `value` to all elements in the fixed array.
298 void fill(const value_type& val) { std::fill(begin(), end(), val); }
299
300 // Relational operators. Equality operators are elementwise using
301 // `operator==`, while order operators order FixedArrays lexicographically.
302 friend bool operator==(const FixedArray& lhs, const FixedArray& rhs) {
303 return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
304 }
305
306 friend bool operator!=(const FixedArray& lhs, const FixedArray& rhs) {
307 return !(lhs == rhs);
308 }
309
310 friend bool operator<(const FixedArray& lhs, const FixedArray& rhs) {
311 return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(),
312 rhs.end());
313 }
314
315 friend bool operator>(const FixedArray& lhs, const FixedArray& rhs) {
316 return rhs < lhs;
317 }
318
319 friend bool operator<=(const FixedArray& lhs, const FixedArray& rhs) {
320 return !(rhs < lhs);
321 }
322
323 friend bool operator>=(const FixedArray& lhs, const FixedArray& rhs) {
324 return !(lhs < rhs);
325 }
326
327 private:
328 // StorageElement
329 //
330 // For FixedArrays with a C-style-array value_type, StorageElement is a POD
331 // wrapper struct called StorageElementWrapper that holds the value_type
332 // instance inside. This is needed for construction and destruction of the
333 // entire array regardless of how many dimensions it has. For all other cases,
334 // StorageElement is just an alias of value_type.
335 //
336 // Maintainer's Note: The simpler solution would be to simply wrap value_type
337 // in a struct whether it's an array or not. That causes some paranoid
338 // diagnostics to misfire, believing that 'data()' returns a pointer to a
339 // single element, rather than the packed array that it really is.
340 // e.g.:
341 //
342 // FixedArray<char> buf(1);
343 // sprintf(buf.data(), "foo");
344 //
345 // error: call to int __builtin___sprintf_chk(etc...)
346 // will always overflow destination buffer [-Werror]
347 //
348 template <typename OuterT,
349 typename InnerT = typename std::remove_extent<OuterT>::type,
350 size_t InnerN = std::extent<OuterT>::value>
352 InnerT array[InnerN];
353 };
354
356 typename std::conditional<std::is_array<value_type>::value,
358 value_type>::type;
359
360 static pointer AsValueType(pointer ptr) { return ptr; }
362 return std::addressof(ptr->array);
363 }
364
365 static_assert(sizeof(StorageElement) == sizeof(value_type), "");
366 static_assert(alignof(StorageElement) == alignof(value_type), "");
367
369 public:
370 StorageElement* data() { return reinterpret_cast<StorageElement*>(buff_); }
373
374 // #ifdef ADDRESS_SANITIZER
375 // void* RedzoneBegin() { return &redzone_begin_; }
376 // void* RedzoneEnd() { return &redzone_end_ + 1; }
377 // #endif // ADDRESS_SANITIZER
378
379 private:
380 // ADDRESS_SANITIZER_REDZONE(redzone_begin_);
382 // ADDRESS_SANITIZER_REDZONE(redzone_end_);
383 };
384
386 public:
387 StorageElement* data() { return nullptr; }
390 };
391
393 typename std::conditional<inline_elements == 0, EmptyInlinedStorage,
395
396 // Storage
397 //
398 // An instance of Storage manages the inline and out-of-line memory for
399 // instances of FixedArray. This guarantees that even when construction of
400 // individual elements fails in the FixedArray constructor body, the
401 // destructor for Storage will still be called and out-of-line memory will be
402 // properly deallocated.
403 //
404 class Storage : public InlinedStorage {
405 public:
408
409 ~Storage() noexcept {
410 if (UsingInlinedStorage(size())) {
411 InlinedStorage::AnnotateDestruct(size());
412 } else {
413 AllocatorTraits::deallocate(alloc(), AsValueType(begin()), size());
414 }
415 }
416
417 size_type size() const { return std::get<0>(size_alloc_); }
418 StorageElement* begin() const { return data_; }
419 StorageElement* end() const { return begin() + size(); }
420 allocator_type& alloc() { return std::get<1>(size_alloc_); }
421
422 private:
424 return n <= inline_elements;
425 }
426
428 if (UsingInlinedStorage(size())) {
429 InlinedStorage::AnnotateConstruct(size());
430 return InlinedStorage::data();
431 }
432 return reinterpret_cast<StorageElement*>(
433 AllocatorTraits::allocate(alloc(), size()));
434 }
435
436 // Using std::tuple and not absl::CompressedTuple, as it has a lot of
437 // dependencies to other absl headers.
438 std::tuple<size_type, allocator_type> size_alloc_;
440 };
441
443};
444
445template <typename T, size_t N, typename A>
447
448template <typename T, size_t N, typename A>
449constexpr typename FixedArray<T, N, A>::size_type
451
452} // namespace internal
453} // namespace ceres
454} // namespace g2o
455
456#endif // G2O_CERES_PUBLIC_INTERNAL_FIXED_ARRAY_H_
char buff_[sizeof(StorageElement[inline_elements])]
std::tuple< size_type, allocator_type > size_alloc_
Storage(size_type n, const allocator_type &a)
static bool UsingInlinedStorage(size_type n)
constexpr size_type max_size() const
typename std::conditional< inline_elements==0, EmptyInlinedStorage, NonEmptyInlinedStorage >::type InlinedStorage
FixedArray(std::initializer_list< value_type > init_list, const allocator_type &a=allocator_type())
const_reverse_iterator crend() const
const value_type & const_reference
typename AllocatorTraits::const_pointer const_pointer
void operator=(FixedArray &&)=delete
static constexpr size_type inline_elements
typename AllocatorTraits::difference_type difference_type
static pointer AsValueType(pointer ptr)
const_iterator begin() const
static pointer AsValueType(StorageElementWrapper< value_type > *ptr)
reference operator[](size_type i)
static constexpr bool DefaultConstructorIsNonTrivial()
Definition fixed_array.h:98
FixedArray(size_type n, const allocator_type &a=allocator_type())
const_reference operator[](size_type i) const
std::allocator_traits< A > AllocatorTraits
Definition fixed_array.h:91
const_reverse_iterator rend() const
friend bool operator!=(const FixedArray &lhs, const FixedArray &rhs)
const_iterator cend() const
FixedArray(const FixedArray &other, const allocator_type &a=allocator_type())
typename AllocatorTraits::value_type value_type
friend bool operator<(const FixedArray &lhs, const FixedArray &rhs)
static constexpr size_t kInlineBytesDefault
Definition fixed_array.h:89
FixedArray(size_type n, const value_type &val, const allocator_type &a=allocator_type())
typename std::enable_if< std::is_convertible< typename std::iterator_traits< Iterator >::iterator_category, std::forward_iterator_tag >::value >::type EnableIfForwardIterator
Definition fixed_array.h:97
typename AllocatorTraits::size_type size_type
const_iterator cbegin() const
typename std::conditional< std::is_array< value_type >::value, StorageElementWrapper< value_type >, value_type >::type StorageElement
void operator=(const FixedArray &)=delete
const_reference back() const
friend bool operator==(const FixedArray &lhs, const FixedArray &rhs)
const_reverse_iterator rbegin() const
const_pointer data() const
typename AllocatorTraits::allocator_type allocator_type
const_reference front() const
const_iterator end() const
const_reverse_iterator crbegin() const
void fill(const value_type &val)
friend bool operator<=(const FixedArray &lhs, const FixedArray &rhs)
friend bool operator>=(const FixedArray &lhs, const FixedArray &rhs)
FixedArray(Iterator first, Iterator last, const allocator_type &a=allocator_type())
friend bool operator>(const FixedArray &lhs, const FixedArray &rhs)
std::reverse_iterator< iterator > reverse_iterator
std::reverse_iterator< const_iterator > const_reverse_iterator
FixedArray(FixedArray &&other, const allocator_type &a=allocator_type()) noexcept
typename AllocatorTraits::pointer pointer
void CopyRange(Allocator &alloc, Iterator destination, InputIterator first, InputIterator last)
Definition memory.h:66
void ConstructRange(Allocator &alloc, Iterator first, Iterator last, const Args &... args)
Definition memory.h:48
static constexpr auto kFixedArrayUseDefault
Definition fixed_array.h:47
typename std::conditional< std::is_trivial< T >::value, std::allocator< T >, Eigen::aligned_allocator< T > >::type FixedArrayDefaultAllocator
Definition fixed_array.h:58
Definition jet.h:876