ceres::internal::FixedArray -> absl::FixedArray Ceres Solver was using an old forked version of FixedArray, now that we are using absl, we can use the official version that ships with it. Change-Id: Ic88d7f6e8a49b928d611f7cbb04172452b322b01
diff --git a/include/ceres/autodiff_first_order_function.h b/include/ceres/autodiff_first_order_function.h index 6cd1b13..2dca8f3 100644 --- a/include/ceres/autodiff_first_order_function.h +++ b/include/ceres/autodiff_first_order_function.h
@@ -34,9 +34,9 @@ #include <memory> #include <type_traits> +#include "absl/container/fixed_array.h" #include "ceres/first_order_function.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/fixed_array.h" #include "ceres/jet.h" #include "ceres/types.h" @@ -131,7 +131,7 @@ } using JetT = Jet<double, kNumParameters>; - internal::FixedArray<JetT, (256 * 7) / sizeof(JetT)> x(kNumParameters); + absl::FixedArray<JetT, (256 * 7) / sizeof(JetT)> x(kNumParameters); for (int i = 0; i < kNumParameters; ++i) { x[i].a = parameters[i]; x[i].v.setZero();
diff --git a/include/ceres/dynamic_autodiff_cost_function.h b/include/ceres/dynamic_autodiff_cost_function.h index 7b4e876..7efdad0 100644 --- a/include/ceres/dynamic_autodiff_cost_function.h +++ b/include/ceres/dynamic_autodiff_cost_function.h
@@ -38,9 +38,9 @@ #include <type_traits> #include <vector> +#include "absl/container/fixed_array.h" #include "absl/log/check.h" #include "ceres/dynamic_cost_function.h" -#include "ceres/internal/fixed_array.h" #include "ceres/jet.h" #include "ceres/types.h" @@ -149,14 +149,13 @@ // Allocate scratch space for the strided evaluation. using JetT = Jet<double, Stride>; - internal::FixedArray<JetT, (256 * 7) / sizeof(JetT)> input_jets( - num_parameters); - internal::FixedArray<JetT, (256 * 7) / sizeof(JetT)> output_jets( + absl::FixedArray<JetT, (256 * 7) / sizeof(JetT)> input_jets(num_parameters); + absl::FixedArray<JetT, (256 * 7) / sizeof(JetT)> output_jets( num_residuals()); // Make the parameter pack that is sent to the functor (reused). - internal::FixedArray<Jet<double, Stride>*> jet_parameters( - num_parameter_blocks, nullptr); + absl::FixedArray<Jet<double, Stride>*> jet_parameters(num_parameter_blocks, + nullptr); int num_active_parameters = 0; // To handle constant parameters between non-constant parameter blocks, the
diff --git a/include/ceres/dynamic_cost_function_to_functor.h b/include/ceres/dynamic_cost_function_to_functor.h index 331d367..bc2fd3b 100644 --- a/include/ceres/dynamic_cost_function_to_functor.h +++ b/include/ceres/dynamic_cost_function_to_functor.h
@@ -36,11 +36,11 @@ #include <numeric> #include <vector> +#include "absl/container/fixed_array.h" #include "absl/log/check.h" #include "ceres/dynamic_cost_function.h" #include "ceres/internal/disable_warnings.h" #include "ceres/internal/export.h" -#include "ceres/internal/fixed_array.h" namespace ceres { @@ -130,11 +130,11 @@ const int num_parameters = std::accumulate( parameter_block_sizes.begin(), parameter_block_sizes.end(), 0); - internal::FixedArray<double> parameters(num_parameters); - internal::FixedArray<double*> parameter_blocks(num_parameter_blocks); - internal::FixedArray<double> jacobians(num_residuals * num_parameters); - internal::FixedArray<double*> jacobian_blocks(num_parameter_blocks); - internal::FixedArray<double> residuals(num_residuals); + absl::FixedArray<double> parameters(num_parameters); + absl::FixedArray<double*> parameter_blocks(num_parameter_blocks); + absl::FixedArray<double> jacobians(num_residuals * num_parameters); + absl::FixedArray<double*> jacobian_blocks(num_parameter_blocks); + absl::FixedArray<double> residuals(num_residuals); // Build a set of arrays to get the residuals and jacobians from // the CostFunction wrapped by this functor.
diff --git a/include/ceres/gradient_checker.h b/include/ceres/gradient_checker.h index 765d53c..7490cd6 100644 --- a/include/ceres/gradient_checker.h +++ b/include/ceres/gradient_checker.h
@@ -43,7 +43,6 @@ #include "ceres/internal/disable_warnings.h" #include "ceres/internal/eigen.h" #include "ceres/internal/export.h" -#include "ceres/internal/fixed_array.h" #include "ceres/manifold.h" namespace ceres {
diff --git a/include/ceres/internal/array_selector.h b/include/ceres/internal/array_selector.h index 9480146..100b80d 100644 --- a/include/ceres/internal/array_selector.h +++ b/include/ceres/internal/array_selector.h
@@ -35,7 +35,8 @@ #include <array> #include <vector> -#include "ceres/internal/fixed_array.h" +#include "absl/container/fixed_array.h" +#include "absl/log/check.h" #include "ceres/types.h" namespace ceres::internal { @@ -47,7 +48,7 @@ // Three different containers are selected in different scenarios: // // num_elements == DYNAMIC: -// -> ceres::internal::FixedArray<T, max_stack_size>(size) +// -> absl::FixedArray<T, max_stack_size>(size) // num_elements != DYNAMIC && num_elements <= max_stack_size // -> std::array<T,num_elements> @@ -71,9 +72,9 @@ max_num_elements_on_stack, true, fits_on_stack> - : ceres::internal::FixedArray<T, max_num_elements_on_stack> { + : absl::FixedArray<T, max_num_elements_on_stack> { explicit ArraySelector(int s) - : ceres::internal::FixedArray<T, max_num_elements_on_stack>(s) {} + : absl::FixedArray<T, max_num_elements_on_stack>(s) {} }; template <typename T, int num_elements, int max_num_elements_on_stack>
diff --git a/include/ceres/internal/fixed_array.h b/include/ceres/internal/fixed_array.h deleted file mode 100644 index 68193c9..0000000 --- a/include/ceres/internal/fixed_array.h +++ /dev/null
@@ -1,467 +0,0 @@ -// Copyright 2018 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// ----------------------------------------------------------------------------- -// File: fixed_array.h -// ----------------------------------------------------------------------------- -// -// A `FixedArray<T>` represents a non-resizable array of `T` where the length of -// the array can be determined at run-time. It is a good replacement for -// non-standard and deprecated uses of `alloca()` and variable length arrays -// within the GCC extension. (See -// https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html). -// -// `FixedArray` allocates small arrays inline, keeping performance fast by -// avoiding heap operations. It also helps reduce the chances of -// accidentally overflowing your stack if large input is passed to -// your function. - -#ifndef CERES_PUBLIC_INTERNAL_FIXED_ARRAY_H_ -#define CERES_PUBLIC_INTERNAL_FIXED_ARRAY_H_ - -#include <Eigen/Core> // For Eigen::aligned_allocator -#include <algorithm> -#include <cstddef> -#include <initializer_list> -#include <iterator> -#include <limits> -#include <memory> -#include <tuple> -#include <type_traits> - -#include "absl/log/check.h" -#include "ceres/internal/memory.h" - -namespace ceres::internal { - -constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1); - -// The default fixed array allocator. -// -// As one can not easily detect if a struct contains or inherits from a fixed -// size Eigen type, to be safe the Eigen::aligned_allocator is used by default. -// But trivial types can never contain Eigen types, so std::allocator is used to -// safe some heap memory. -template <typename T> -using FixedArrayDefaultAllocator = - typename std::conditional<std::is_trivial<T>::value, - std::allocator<T>, - Eigen::aligned_allocator<T>>::type; - -// ----------------------------------------------------------------------------- -// FixedArray -// ----------------------------------------------------------------------------- -// -// A `FixedArray` provides a run-time fixed-size array, allocating a small array -// inline for efficiency. -// -// Most users should not specify an `inline_elements` argument and let -// `FixedArray` automatically determine the number of elements -// to store inline based on `sizeof(T)`. If `inline_elements` is specified, the -// `FixedArray` implementation will use inline storage for arrays with a -// length <= `inline_elements`. -// -// Note that a `FixedArray` constructed with a `size_type` argument will -// default-initialize its values by leaving trivially constructible types -// uninitialized (e.g. int, int[4], double), and others default-constructed. -// This matches the behavior of c-style arrays and `std::array`, but not -// `std::vector`. -// -// Note that `FixedArray` does not provide a public allocator; if it requires a -// heap allocation, it will do so with global `::operator new[]()` and -// `::operator delete[]()`, even if T provides class-scope overrides for these -// operators. -template <typename T, - size_t N = kFixedArrayUseDefault, - typename A = FixedArrayDefaultAllocator<T>> -class FixedArray { - static_assert(!std::is_array<T>::value || std::extent<T>::value > 0, - "Arrays with unknown bounds cannot be used with FixedArray."); - - static constexpr size_t kInlineBytesDefault = 256; - - using AllocatorTraits = std::allocator_traits<A>; - // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17, - // but this seems to be mostly pedantic. - template <typename Iterator> - using EnableIfForwardIterator = typename std::enable_if<std::is_convertible< - typename std::iterator_traits<Iterator>::iterator_category, - std::forward_iterator_tag>::value>::type; - static constexpr bool DefaultConstructorIsNonTrivial() { - return !std::is_trivially_default_constructible<StorageElement>::value; - } - - public: - using allocator_type = typename AllocatorTraits::allocator_type; - using value_type = typename AllocatorTraits::value_type; - using pointer = typename AllocatorTraits::pointer; - using const_pointer = typename AllocatorTraits::const_pointer; - using reference = value_type&; - using const_reference = const value_type&; - using size_type = typename AllocatorTraits::size_type; - using difference_type = typename AllocatorTraits::difference_type; - 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 size_type inline_elements = - (N == kFixedArrayUseDefault ? kInlineBytesDefault / sizeof(value_type) - : static_cast<size_type>(N)); - - FixedArray(const FixedArray& other, - const allocator_type& a = allocator_type()) - : FixedArray(other.begin(), other.end(), a) {} - - FixedArray(FixedArray&& other, const allocator_type& a = allocator_type()) - : FixedArray(std::make_move_iterator(other.begin()), - std::make_move_iterator(other.end()), - a) {} - - // Creates an array object that can store `n` elements. - // Note that trivially constructible elements will be uninitialized. - explicit FixedArray(size_type n, const allocator_type& a = allocator_type()) - : storage_(n, a) { - if (DefaultConstructorIsNonTrivial()) { - ConstructRange(storage_.alloc(), storage_.begin(), storage_.end()); - } - } - - // Creates an array initialized with `n` copies of `val`. - FixedArray(size_type n, - const value_type& val, - const allocator_type& a = allocator_type()) - : storage_(n, a) { - ConstructRange(storage_.alloc(), storage_.begin(), storage_.end(), val); - } - - // Creates an array initialized with the size and contents of `init_list`. - FixedArray(std::initializer_list<value_type> init_list, - const allocator_type& a = allocator_type()) - : FixedArray(init_list.begin(), init_list.end(), a) {} - - // Creates an array initialized with the elements from the input - // range. The array's size will always be `std::distance(first, last)`. - // REQUIRES: Iterator must be a forward_iterator or better. - template <typename Iterator, EnableIfForwardIterator<Iterator>* = nullptr> - FixedArray(Iterator first, - Iterator last, - const allocator_type& a = allocator_type()) - : storage_(std::distance(first, last), a) { - CopyRange(storage_.alloc(), storage_.begin(), first, last); - } - - ~FixedArray() noexcept { - for (auto* cur = storage_.begin(); cur != storage_.end(); ++cur) { - AllocatorTraits::destroy(storage_.alloc(), cur); - } - } - - // Assignments are deleted because they break the invariant that the size of a - // `FixedArray` never changes. - void operator=(FixedArray&&) = delete; - void operator=(const FixedArray&) = delete; - - // FixedArray::size() - // - // Returns the length of the fixed array. - size_type size() const { return storage_.size(); } - - // FixedArray::max_size() - // - // Returns the largest possible value of `std::distance(begin(), end())` for a - // `FixedArray<T>`. This is equivalent to the most possible addressable bytes - // over the number of bytes taken by T. - constexpr size_type max_size() const { - return (std::numeric_limits<difference_type>::max)() / sizeof(value_type); - } - - // FixedArray::empty() - // - // Returns whether or not the fixed array is empty. - bool empty() const { return size() == 0; } - - // FixedArray::memsize() - // - // Returns the memory size of the fixed array in bytes. - size_t memsize() const { return size() * sizeof(value_type); } - - // FixedArray::data() - // - // Returns a const T* pointer to elements of the `FixedArray`. This pointer - // can be used to access (but not modify) the contained elements. - const_pointer data() const { return AsValueType(storage_.begin()); } - - // Overload of FixedArray::data() to return a T* pointer to elements of the - // fixed array. This pointer can be used to access and modify the contained - // elements. - pointer data() { return AsValueType(storage_.begin()); } - - // FixedArray::operator[] - // - // Returns a reference the ith element of the fixed array. - // REQUIRES: 0 <= i < size() - reference operator[](size_type i) { - DCHECK_LT(i, size()); - return data()[i]; - } - - // Overload of FixedArray::operator()[] to return a const reference to the - // ith element of the fixed array. - // REQUIRES: 0 <= i < size() - const_reference operator[](size_type i) const { - DCHECK_LT(i, size()); - return data()[i]; - } - - // FixedArray::front() - // - // Returns a reference to the first element of the fixed array. - reference front() { return *begin(); } - - // Overload of FixedArray::front() to return a reference to the first element - // of a fixed array of const values. - const_reference front() const { return *begin(); } - - // FixedArray::back() - // - // Returns a reference to the last element of the fixed array. - reference back() { return *(end() - 1); } - - // Overload of FixedArray::back() to return a reference to the last element - // of a fixed array of const values. - const_reference back() const { return *(end() - 1); } - - // FixedArray::begin() - // - // Returns an iterator to the beginning of the fixed array. - iterator begin() { return data(); } - - // Overload of FixedArray::begin() to return a const iterator to the - // beginning of the fixed array. - const_iterator begin() const { return data(); } - - // FixedArray::cbegin() - // - // Returns a const iterator to the beginning of the fixed array. - const_iterator cbegin() const { return begin(); } - - // FixedArray::end() - // - // Returns an iterator to the end of the fixed array. - iterator end() { return data() + size(); } - - // Overload of FixedArray::end() to return a const iterator to the end of the - // fixed array. - const_iterator end() const { return data() + size(); } - - // FixedArray::cend() - // - // Returns a const iterator to the end of the fixed array. - const_iterator cend() const { return end(); } - - // FixedArray::rbegin() - // - // Returns a reverse iterator from the end of the fixed array. - reverse_iterator rbegin() { return reverse_iterator(end()); } - - // Overload of FixedArray::rbegin() to return a const reverse iterator from - // the end of the fixed array. - const_reverse_iterator rbegin() const { - return const_reverse_iterator(end()); - } - - // FixedArray::crbegin() - // - // Returns a const reverse iterator from the end of the fixed array. - const_reverse_iterator crbegin() const { return rbegin(); } - - // FixedArray::rend() - // - // Returns a reverse iterator from the beginning of the fixed array. - reverse_iterator rend() { return reverse_iterator(begin()); } - - // Overload of FixedArray::rend() for returning a const reverse iterator - // from the beginning of the fixed array. - const_reverse_iterator rend() const { - return const_reverse_iterator(begin()); - } - - // FixedArray::crend() - // - // Returns a reverse iterator from the beginning of the fixed array. - const_reverse_iterator crend() const { return rend(); } - - // FixedArray::fill() - // - // Assigns the given `value` to all elements in the fixed array. - void fill(const value_type& val) { std::fill(begin(), end(), val); } - - // Relational operators. Equality operators are elementwise using - // `operator==`, while order operators order FixedArrays lexicographically. - friend bool operator==(const FixedArray& lhs, const FixedArray& rhs) { - return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); - } - - friend bool operator!=(const FixedArray& lhs, const FixedArray& rhs) { - return !(lhs == rhs); - } - - friend bool operator<(const FixedArray& lhs, const FixedArray& rhs) { - return std::lexicographical_compare( - lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); - } - - friend bool operator>(const FixedArray& lhs, const FixedArray& rhs) { - return rhs < lhs; - } - - friend bool operator<=(const FixedArray& lhs, const FixedArray& rhs) { - return !(rhs < lhs); - } - - friend bool operator>=(const FixedArray& lhs, const FixedArray& rhs) { - return !(lhs < rhs); - } - - private: - // StorageElement - // - // For FixedArrays with a C-style-array value_type, StorageElement is a POD - // wrapper struct called StorageElementWrapper that holds the value_type - // instance inside. This is needed for construction and destruction of the - // entire array regardless of how many dimensions it has. For all other cases, - // StorageElement is just an alias of value_type. - // - // Maintainer's Note: The simpler solution would be to simply wrap value_type - // in a struct whether it's an array or not. That causes some paranoid - // diagnostics to misfire, believing that 'data()' returns a pointer to a - // single element, rather than the packed array that it really is. - // e.g.: - // - // FixedArray<char> buf(1); - // sprintf(buf.data(), "foo"); - // - // error: call to int __builtin___sprintf_chk(etc...) - // will always overflow destination buffer [-Werror] - // - template <typename OuterT, - typename InnerT = typename std::remove_extent<OuterT>::type, - size_t InnerN = std::extent<OuterT>::value> - struct StorageElementWrapper { - InnerT array[InnerN]; - }; - - using StorageElement = - typename std::conditional<std::is_array<value_type>::value, - StorageElementWrapper<value_type>, - value_type>::type; - - static pointer AsValueType(pointer ptr) { return ptr; } - static pointer AsValueType(StorageElementWrapper<value_type>* ptr) { - return std::addressof(ptr->array); - } - - static_assert(sizeof(StorageElement) == sizeof(value_type)); - static_assert(alignof(StorageElement) == alignof(value_type)); - - class NonEmptyInlinedStorage { - public: - StorageElement* data() { return reinterpret_cast<StorageElement*>(buff_); } - void AnnotateConstruct(size_type) {} - void AnnotateDestruct(size_type) {} - - // #ifdef ADDRESS_SANITIZER - // void* RedzoneBegin() { return &redzone_begin_; } - // void* RedzoneEnd() { return &redzone_end_ + 1; } - // #endif // ADDRESS_SANITIZER - - private: - // ADDRESS_SANITIZER_REDZONE(redzone_begin_); - alignas(StorageElement) char buff_[sizeof(StorageElement[inline_elements])]; - // ADDRESS_SANITIZER_REDZONE(redzone_end_); - }; - - class EmptyInlinedStorage { - public: - StorageElement* data() { return nullptr; } - void AnnotateConstruct(size_type) {} - void AnnotateDestruct(size_type) {} - }; - - using InlinedStorage = - typename std::conditional<inline_elements == 0, - EmptyInlinedStorage, - NonEmptyInlinedStorage>::type; - - // Storage - // - // An instance of Storage manages the inline and out-of-line memory for - // instances of FixedArray. This guarantees that even when construction of - // individual elements fails in the FixedArray constructor body, the - // destructor for Storage will still be called and out-of-line memory will be - // properly deallocated. - // - class Storage : public InlinedStorage { - public: - Storage(size_type n, const allocator_type& a) - : size_alloc_(n, a), data_(InitializeData()) {} - - ~Storage() noexcept { - if (UsingInlinedStorage(size())) { - InlinedStorage::AnnotateDestruct(size()); - } else { - AllocatorTraits::deallocate(alloc(), AsValueType(begin()), size()); - } - } - - size_type size() const { return std::get<0>(size_alloc_); } - StorageElement* begin() const { return data_; } - StorageElement* end() const { return begin() + size(); } - allocator_type& alloc() { return std::get<1>(size_alloc_); } - - private: - static bool UsingInlinedStorage(size_type n) { - return n <= inline_elements; - } - - StorageElement* InitializeData() { - if (UsingInlinedStorage(size())) { - InlinedStorage::AnnotateConstruct(size()); - return InlinedStorage::data(); - } else { - return reinterpret_cast<StorageElement*>( - AllocatorTraits::allocate(alloc(), size())); - } - } - - // Using std::tuple and not absl::CompressedTuple, as it has a lot of - // dependencies to other absl headers. - std::tuple<size_type, allocator_type> size_alloc_; - StorageElement* data_; - }; - - Storage storage_; -}; - -template <typename T, size_t N, typename A> -constexpr size_t FixedArray<T, N, A>::kInlineBytesDefault; - -template <typename T, size_t N, typename A> -constexpr typename FixedArray<T, N, A>::size_type - FixedArray<T, N, A>::inline_elements; - -} // namespace ceres::internal - -#endif // CERES_PUBLIC_INTERNAL_FIXED_ARRAY_H_
diff --git a/include/ceres/internal/numeric_diff.h b/include/ceres/internal/numeric_diff.h index 4031631..976b27f 100644 --- a/include/ceres/internal/numeric_diff.h +++ b/include/ceres/internal/numeric_diff.h
@@ -40,9 +40,9 @@ #include "Eigen/Dense" #include "Eigen/StdVector" +#include "absl/container/fixed_array.h" #include "absl/log/check.h" #include "ceres/cost_function.h" -#include "ceres/internal/fixed_array.h" #include "ceres/internal/variadic_evaluate.h" #include "ceres/numeric_diff_options.h" #include "ceres/types.h" @@ -125,8 +125,8 @@ // For each parameter in the parameter block, use finite differences to // compute the derivative for that parameter. - FixedArray<double> temp_residual_array(num_residuals_internal); - FixedArray<double> residual_array(num_residuals_internal); + absl::FixedArray<double> temp_residual_array(num_residuals_internal); + absl::FixedArray<double> residual_array(num_residuals_internal); Map<ResidualVector> residuals(residual_array.data(), num_residuals_internal);
diff --git a/include/ceres/numeric_diff_cost_function.h b/include/ceres/numeric_diff_cost_function.h index f2a377b..47e4af4 100644 --- a/include/ceres/numeric_diff_cost_function.h +++ b/include/ceres/numeric_diff_cost_function.h
@@ -228,7 +228,7 @@ bool Evaluate(double const* const* parameters, double* residuals, double** jacobians) const override { - using internal::FixedArray; + using absl::FixedArray; using internal::NumericDiff; using ParameterDims =
diff --git a/include/ceres/numeric_diff_first_order_function.h b/include/ceres/numeric_diff_first_order_function.h index 790cbcd..a9a7798 100644 --- a/include/ceres/numeric_diff_first_order_function.h +++ b/include/ceres/numeric_diff_first_order_function.h
@@ -36,10 +36,10 @@ #include <type_traits> #include <utility> +#include "absl/container/fixed_array.h" #include "absl/log/check.h" #include "ceres/first_order_function.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/fixed_array.h" #include "ceres/internal/numeric_diff.h" #include "ceres/internal/parameter_dims.h" #include "ceres/internal/variadic_evaluate.h" @@ -197,7 +197,7 @@ } // Create a copy of the parameters which will get mutated. - internal::FixedArray<double, 32> parameters_copy(num_parameters_); + absl::FixedArray<double> parameters_copy(num_parameters_); std::copy_n(parameters, num_parameters_, parameters_copy.data()); double* parameters_ptr = parameters_copy.data(); constexpr int kNumResiduals = 1;
diff --git a/include/ceres/product_manifold.h b/include/ceres/product_manifold.h index ed2d1f4..9fc836e 100644 --- a/include/ceres/product_manifold.h +++ b/include/ceres/product_manifold.h
@@ -42,8 +42,8 @@ #include <type_traits> #include <utility> +#include "absl/container/fixed_array.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/fixed_array.h" #include "ceres/internal/port.h" #include "ceres/manifold.h" @@ -117,7 +117,7 @@ bool PlusJacobian(const double* x, double* jacobian_ptr) const override { MatrixRef jacobian(jacobian_ptr, AmbientSize(), TangentSize()); jacobian.setZero(); - internal::FixedArray<double> buffer(buffer_size_); + absl::FixedArray<double> buffer(buffer_size_); return PlusJacobianImpl( x, jacobian, buffer, std::make_index_sequence<kNumManifolds>{}); @@ -126,7 +126,7 @@ bool MinusJacobian(const double* x, double* jacobian_ptr) const override { MatrixRef jacobian(jacobian_ptr, TangentSize(), AmbientSize()); jacobian.setZero(); - internal::FixedArray<double> buffer(buffer_size_); + absl::FixedArray<double> buffer(buffer_size_); return MinusJacobianImpl( x, jacobian, buffer, std::make_index_sequence<kNumManifolds>{}); @@ -199,7 +199,7 @@ template <std::size_t Index0, std::size_t... Indices> bool PlusJacobianImpl(const double* x, MatrixRef& jacobian, - internal::FixedArray<double>& buffer, + absl::FixedArray<double>& buffer, std::index_sequence<Index0, Indices...>) const { if (!Dereference(std::get<Index0>(manifolds_)) .PlusJacobian(x + ambient_offsets_[Index0], buffer.data())) { @@ -217,18 +217,17 @@ x, jacobian, buffer, std::index_sequence<Indices...>{}); } - static constexpr bool PlusJacobianImpl( - const double* /*x*/, - MatrixRef& /*jacobian*/, - internal::FixedArray<double>& /*buffer*/, - std::index_sequence<>) noexcept { + static constexpr bool PlusJacobianImpl(const double* /*x*/, + MatrixRef& /*jacobian*/, + absl::FixedArray<double>& /*buffer*/, + std::index_sequence<>) noexcept { return true; } template <std::size_t Index0, std::size_t... Indices> bool MinusJacobianImpl(const double* x, MatrixRef& jacobian, - internal::FixedArray<double>& buffer, + absl::FixedArray<double>& buffer, std::index_sequence<Index0, Indices...>) const { if (!Dereference(std::get<Index0>(manifolds_)) .MinusJacobian(x + ambient_offsets_[Index0], buffer.data())) { @@ -246,11 +245,10 @@ x, jacobian, buffer, std::index_sequence<Indices...>{}); } - static constexpr bool MinusJacobianImpl( - const double* /*x*/, - MatrixRef& /*jacobian*/, - internal::FixedArray<double>& /*buffer*/, - std::index_sequence<>) noexcept { + static constexpr bool MinusJacobianImpl(const double* /*x*/, + MatrixRef& /*jacobian*/, + absl::FixedArray<double>& /*buffer*/, + std::index_sequence<>) noexcept { return true; }
diff --git a/internal/ceres/CMakeLists.txt b/internal/ceres/CMakeLists.txt index f30ba00..808e0ed 100644 --- a/internal/ceres/CMakeLists.txt +++ b/internal/ceres/CMakeLists.txt
@@ -39,6 +39,7 @@ list(APPEND CERES_LIBRARY_PUBLIC_DEPENDENCIES absl::log) list(APPEND CERES_LIBRARY_PUBLIC_DEPENDENCIES absl::check) +list(APPEND CERES_LIBRARY_PUBLIC_DEPENDENCIES absl::fixed_array) # Source files that contain public symbols and live in the ceres namespaces. # Such symbols are expected to be marked with CERES_EXPORT and the files below @@ -450,7 +451,6 @@ ceres_test(dynamic_sparsity) ceres_test(evaluation_callback) ceres_test(evaluator) - ceres_test(fixed_array) ceres_test(gradient_checker) ceres_test(gradient_checking_cost_function) ceres_test(gradient_problem)
diff --git a/internal/ceres/array_selector_test.cc b/internal/ceres/array_selector_test.cc index 36155f1..0b6c893 100644 --- a/internal/ceres/array_selector_test.cc +++ b/internal/ceres/array_selector_test.cc
@@ -35,7 +35,7 @@ #include <type_traits> #include <vector> -#include "ceres/internal/fixed_array.h" +#include "absl/container/fixed_array.h" #include "ceres/types.h" #include "gtest/gtest.h" @@ -47,12 +47,12 @@ TEST(ArraySelector, FixedArray) { ArraySelector<int, DYNAMIC, 20> array1(10); static_assert( - std::is_base_of<internal::FixedArray<int, 20>, decltype(array1)>::value); + std::is_base_of<absl::FixedArray<int, 20>, decltype(array1)>::value); EXPECT_EQ(array1.size(), 10); ArraySelector<int, DYNAMIC, 10> array2(20); static_assert( - std::is_base_of<internal::FixedArray<int, 10>, decltype(array2)>::value); + std::is_base_of<absl::FixedArray<int, 10>, decltype(array2)>::value); EXPECT_EQ(array2.size(), 20); }
diff --git a/internal/ceres/autodiff_test.cc b/internal/ceres/autodiff_test.cc index b50327c..787adf8 100644 --- a/internal/ceres/autodiff_test.cc +++ b/internal/ceres/autodiff_test.cc
@@ -34,6 +34,7 @@ #include <iterator> #include <random> +#include "absl/container/fixed_array.h" #include "gtest/gtest.h" namespace ceres::internal { @@ -666,7 +667,7 @@ y += 1; using JetT = Jet<double, 2>; - FixedArray<JetT, (256 * 7) / sizeof(JetT)> x(3); + absl::FixedArray<JetT, (256 * 7) / sizeof(JetT)> x(3); // Need this to makes sure that x does not get optimized out. x[0] = x[0] + JetT(1.0);
diff --git a/internal/ceres/eigen_vector_ops.h b/internal/ceres/eigen_vector_ops.h index 6ebff88..cc0a59b 100644 --- a/internal/ceres/eigen_vector_ops.h +++ b/internal/ceres/eigen_vector_ops.h
@@ -33,8 +33,8 @@ #include <numeric> +#include "absl/container/fixed_array.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/fixed_array.h" #include "ceres/parallel_for.h" #include "ceres/parallel_vector_ops.h" @@ -47,7 +47,7 @@ inline double Norm(const Eigen::DenseBase<Derived>& x, ContextImpl* context, int num_threads) { - FixedArray<double> norms(num_threads, 0.); + absl::FixedArray<double> norms(num_threads, 0.); ParallelFor( context, 0, @@ -77,7 +77,7 @@ const VectorLikeY& y, ContextImpl* context, int num_threads) { - FixedArray<double> dots(num_threads, 0.); + absl::FixedArray<double> dots(num_threads, 0.); ParallelFor( context, 0,
diff --git a/internal/ceres/fixed_array_test.cc b/internal/ceres/fixed_array_test.cc deleted file mode 100644 index 66b3fbf..0000000 --- a/internal/ceres/fixed_array_test.cc +++ /dev/null
@@ -1,835 +0,0 @@ -// Copyright 2017 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ceres/internal/fixed_array.h" - -#include <cstdio> -#include <cstring> -#include <list> -#include <memory> -#include <numeric> -#include <scoped_allocator> -#include <stdexcept> -#include <string> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -using ::testing::ElementsAreArray; - -namespace { - -// CERES_INTERNAL_ARRAYSIZE() -// -// Returns the number of elements in an array as a compile-time constant, which -// can be used in defining new arrays. If you use this macro on a pointer by -// mistake, you will get a compile-time error. -#define CERES_INTERNAL_ARRAYSIZE(array) (sizeof(ArraySizeHelper(array))) - -// Note: this internal template function declaration is used by -// CERES_INTERNAL_ARRAYSIZE. The function doesn't need a definition, as we only -// use its type. -template <typename T, size_t N> -auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N]; - -// Helper routine to determine if a ceres::internal::FixedArray used stack -// allocation. -template <typename ArrayType> -static bool IsOnStack(const ArrayType& a) { - return a.size() <= ArrayType::inline_elements; -} - -class ConstructionTester { - public: - ConstructionTester() : self_ptr_(this) { constructions++; } - ~ConstructionTester() { - assert(self_ptr_ == this); - self_ptr_ = nullptr; - destructions++; - } - - // These are incremented as elements are constructed and destructed so we can - // be sure all elements are properly cleaned up. - static int constructions; - static int destructions; - - void CheckConstructed() { assert(self_ptr_ == this); } - - void set(int value) { value_ = value; } - int get() { return value_; } - - private: - // self_ptr_ should always point to 'this' -- that's how we can be sure the - // constructor has been called. - ConstructionTester* self_ptr_; - int value_{0}; -}; - -int ConstructionTester::constructions = 0; -int ConstructionTester::destructions = 0; - -// ThreeInts will initialize its three ints to the value stored in -// ThreeInts::counter. The constructor increments counter so that each object -// in an array of ThreeInts will have different values. -class ThreeInts { - public: - ThreeInts() { - x_ = counter; - y_ = counter; - z_ = counter; - ++counter; - } - - static int counter; - - int x_, y_, z_; -}; - -int ThreeInts::counter = 0; - -TEST(FixedArrayTest, CopyCtor) { - ceres::internal::FixedArray<int, 10> on_stack(5); - std::iota(on_stack.begin(), on_stack.end(), 0); - ceres::internal::FixedArray<int, 10> stack_copy = on_stack; - EXPECT_THAT(stack_copy, ElementsAreArray(on_stack)); - EXPECT_TRUE(IsOnStack(stack_copy)); - - ceres::internal::FixedArray<int, 10> allocated(15); - std::iota(allocated.begin(), allocated.end(), 0); - ceres::internal::FixedArray<int, 10> alloced_copy = allocated; - EXPECT_THAT(alloced_copy, ElementsAreArray(allocated)); - EXPECT_FALSE(IsOnStack(alloced_copy)); -} - -TEST(FixedArrayTest, MoveCtor) { - ceres::internal::FixedArray<std::unique_ptr<int>, 10> on_stack(5); - for (int i = 0; i < 5; ++i) { - on_stack[i] = std::make_unique<int>(i); - } - - ceres::internal::FixedArray<std::unique_ptr<int>, 10> stack_copy = - std::move(on_stack); - for (int i = 0; i < 5; ++i) EXPECT_EQ(*(stack_copy[i]), i); - EXPECT_EQ(stack_copy.size(), on_stack.size()); - - ceres::internal::FixedArray<std::unique_ptr<int>, 10> allocated(15); - for (int i = 0; i < 15; ++i) { - allocated[i] = std::make_unique<int>(i); - } - - ceres::internal::FixedArray<std::unique_ptr<int>, 10> alloced_copy = - std::move(allocated); - for (int i = 0; i < 15; ++i) EXPECT_EQ(*(alloced_copy[i]), i); - EXPECT_EQ(allocated.size(), alloced_copy.size()); -} - -TEST(FixedArrayTest, SmallObjects) { - // Small object arrays - { - // Short arrays should be on the stack - ceres::internal::FixedArray<int> array(4); - EXPECT_TRUE(IsOnStack(array)); - } - - { - // Large arrays should be on the heap - ceres::internal::FixedArray<int> array(1048576); - EXPECT_FALSE(IsOnStack(array)); - } - - { - // Arrays of <= default size should be on the stack - ceres::internal::FixedArray<int, 100> array(100); - EXPECT_TRUE(IsOnStack(array)); - } - - { - // Arrays of > default size should be on the heap - ceres::internal::FixedArray<int, 100> array(101); - EXPECT_FALSE(IsOnStack(array)); - } - - { - // Arrays with different size elements should use approximately - // same amount of stack space - ceres::internal::FixedArray<int> array1(0); - ceres::internal::FixedArray<char> array2(0); - EXPECT_LE(sizeof(array1), sizeof(array2) + 100); - EXPECT_LE(sizeof(array2), sizeof(array1) + 100); - } - - { - // Ensure that vectors are properly constructed inside a fixed array. - ceres::internal::FixedArray<std::vector<int>> array(2); - EXPECT_EQ(0, array[0].size()); - EXPECT_EQ(0, array[1].size()); - } - - { - // Regardless of ceres::internal::FixedArray implementation, check that a - // type with a low alignment requirement and a non power-of-two size is - // initialized correctly. - ThreeInts::counter = 1; - ceres::internal::FixedArray<ThreeInts> array(2); - EXPECT_EQ(1, array[0].x_); - EXPECT_EQ(1, array[0].y_); - EXPECT_EQ(1, array[0].z_); - EXPECT_EQ(2, array[1].x_); - EXPECT_EQ(2, array[1].y_); - EXPECT_EQ(2, array[1].z_); - } -} - -TEST(FixedArrayRelationalsTest, EqualArrays) { - for (int i = 0; i < 10; ++i) { - ceres::internal::FixedArray<int, 5> a1(i); - std::iota(a1.begin(), a1.end(), 0); - ceres::internal::FixedArray<int, 5> a2(a1.begin(), a1.end()); - - EXPECT_TRUE(a1 == a2); - EXPECT_FALSE(a1 != a2); - EXPECT_TRUE(a2 == a1); - EXPECT_FALSE(a2 != a1); - EXPECT_FALSE(a1 < a2); - EXPECT_FALSE(a1 > a2); - EXPECT_FALSE(a2 < a1); - EXPECT_FALSE(a2 > a1); - EXPECT_TRUE(a1 <= a2); - EXPECT_TRUE(a1 >= a2); - EXPECT_TRUE(a2 <= a1); - EXPECT_TRUE(a2 >= a1); - } -} - -TEST(FixedArrayRelationalsTest, UnequalArrays) { - for (int i = 1; i < 10; ++i) { - ceres::internal::FixedArray<int, 5> a1(i); - std::iota(a1.begin(), a1.end(), 0); - ceres::internal::FixedArray<int, 5> a2(a1.begin(), a1.end()); - --a2[i / 2]; - - EXPECT_FALSE(a1 == a2); - EXPECT_TRUE(a1 != a2); - EXPECT_FALSE(a2 == a1); - EXPECT_TRUE(a2 != a1); - EXPECT_FALSE(a1 < a2); - EXPECT_TRUE(a1 > a2); - EXPECT_TRUE(a2 < a1); - EXPECT_FALSE(a2 > a1); - EXPECT_FALSE(a1 <= a2); - EXPECT_TRUE(a1 >= a2); - EXPECT_TRUE(a2 <= a1); - EXPECT_FALSE(a2 >= a1); - } -} - -template <int stack_elements> -static void TestArray(int n) { - SCOPED_TRACE(n); - SCOPED_TRACE(stack_elements); - ConstructionTester::constructions = 0; - ConstructionTester::destructions = 0; - { - ceres::internal::FixedArray<ConstructionTester, stack_elements> array(n); - - EXPECT_THAT(array.size(), n); - EXPECT_THAT(array.memsize(), sizeof(ConstructionTester) * n); - EXPECT_THAT(array.begin() + n, array.end()); - - // Check that all elements were constructed - for (int i = 0; i < n; i++) { - array[i].CheckConstructed(); - } - // Check that no other elements were constructed - EXPECT_THAT(ConstructionTester::constructions, n); - - // Test operator[] - for (int i = 0; i < n; i++) { - array[i].set(i); - } - for (int i = 0; i < n; i++) { - EXPECT_THAT(array[i].get(), i); - EXPECT_THAT(array.data()[i].get(), i); - } - - // Test data() - for (int i = 0; i < n; i++) { - array.data()[i].set(i + 1); - } - for (int i = 0; i < n; i++) { - EXPECT_THAT(array[i].get(), i + 1); - EXPECT_THAT(array.data()[i].get(), i + 1); - } - } // Close scope containing 'array'. - - // Check that all constructed elements were destructed. - EXPECT_EQ(ConstructionTester::constructions, - ConstructionTester::destructions); -} - -template <int elements_per_inner_array, int inline_elements> -static void TestArrayOfArrays(int n) { - SCOPED_TRACE(n); - SCOPED_TRACE(inline_elements); - SCOPED_TRACE(elements_per_inner_array); - ConstructionTester::constructions = 0; - ConstructionTester::destructions = 0; - { - using InnerArray = ConstructionTester[elements_per_inner_array]; - // Heap-allocate the FixedArray to avoid blowing the stack frame. - auto array_ptr = std::unique_ptr< - ceres::internal::FixedArray<InnerArray, inline_elements>>( - new ceres::internal::FixedArray<InnerArray, inline_elements>(n)); - auto& array = *array_ptr; - - ASSERT_EQ(array.size(), n); - ASSERT_EQ(array.memsize(), - sizeof(ConstructionTester) * elements_per_inner_array * n); - ASSERT_EQ(array.begin() + n, array.end()); - - // Check that all elements were constructed - for (int i = 0; i < n; i++) { - for (int j = 0; j < elements_per_inner_array; j++) { - (array[i])[j].CheckConstructed(); - } - } - // Check that no other elements were constructed - ASSERT_EQ(ConstructionTester::constructions, n * elements_per_inner_array); - - // Test operator[] - for (int i = 0; i < n; i++) { - for (int j = 0; j < elements_per_inner_array; j++) { - (array[i])[j].set(i * elements_per_inner_array + j); - } - } - for (int i = 0; i < n; i++) { - for (int j = 0; j < elements_per_inner_array; j++) { - ASSERT_EQ((array[i])[j].get(), i * elements_per_inner_array + j); - ASSERT_EQ((array.data()[i])[j].get(), i * elements_per_inner_array + j); - } - } - - // Test data() - for (int i = 0; i < n; i++) { - for (int j = 0; j < elements_per_inner_array; j++) { - (array.data()[i])[j].set((i + 1) * elements_per_inner_array + j); - } - } - for (int i = 0; i < n; i++) { - for (int j = 0; j < elements_per_inner_array; j++) { - ASSERT_EQ((array[i])[j].get(), (i + 1) * elements_per_inner_array + j); - ASSERT_EQ((array.data()[i])[j].get(), - (i + 1) * elements_per_inner_array + j); - } - } - } // Close scope containing 'array'. - - // Check that all constructed elements were destructed. - EXPECT_EQ(ConstructionTester::constructions, - ConstructionTester::destructions); -} - -TEST(IteratorConstructorTest, NonInline) { - int const kInput[] = {2, 3, 5, 7, 11, 13, 17}; - ceres::internal::FixedArray<int, CERES_INTERNAL_ARRAYSIZE(kInput) - 1> const - fixed(kInput, kInput + CERES_INTERNAL_ARRAYSIZE(kInput)); - ASSERT_EQ(CERES_INTERNAL_ARRAYSIZE(kInput), fixed.size()); - for (size_t i = 0; i < CERES_INTERNAL_ARRAYSIZE(kInput); ++i) { - ASSERT_EQ(kInput[i], fixed[i]); - } -} - -TEST(IteratorConstructorTest, Inline) { - int const kInput[] = {2, 3, 5, 7, 11, 13, 17}; - ceres::internal::FixedArray<int, CERES_INTERNAL_ARRAYSIZE(kInput)> const - fixed(kInput, kInput + CERES_INTERNAL_ARRAYSIZE(kInput)); - ASSERT_EQ(CERES_INTERNAL_ARRAYSIZE(kInput), fixed.size()); - for (size_t i = 0; i < CERES_INTERNAL_ARRAYSIZE(kInput); ++i) { - ASSERT_EQ(kInput[i], fixed[i]); - } -} - -TEST(IteratorConstructorTest, NonPod) { - char const* kInput[] = { - "red", "orange", "yellow", "green", "blue", "indigo", "violet"}; - ceres::internal::FixedArray<std::string> const fixed( - kInput, kInput + CERES_INTERNAL_ARRAYSIZE(kInput)); - ASSERT_EQ(CERES_INTERNAL_ARRAYSIZE(kInput), fixed.size()); - for (size_t i = 0; i < CERES_INTERNAL_ARRAYSIZE(kInput); ++i) { - ASSERT_EQ(kInput[i], fixed[i]); - } -} - -TEST(IteratorConstructorTest, FromEmptyVector) { - std::vector<int> const empty; - ceres::internal::FixedArray<int> const fixed(empty.begin(), empty.end()); - EXPECT_EQ(0, fixed.size()); - EXPECT_EQ(empty.size(), fixed.size()); -} - -TEST(IteratorConstructorTest, FromNonEmptyVector) { - int const kInput[] = {2, 3, 5, 7, 11, 13, 17}; - std::vector<int> const items(kInput, - kInput + CERES_INTERNAL_ARRAYSIZE(kInput)); - ceres::internal::FixedArray<int> const fixed(items.begin(), items.end()); - ASSERT_EQ(items.size(), fixed.size()); - for (size_t i = 0; i < items.size(); ++i) { - ASSERT_EQ(items[i], fixed[i]); - } -} - -TEST(IteratorConstructorTest, FromBidirectionalIteratorRange) { - int const kInput[] = {2, 3, 5, 7, 11, 13, 17}; - std::list<int> const items(kInput, kInput + CERES_INTERNAL_ARRAYSIZE(kInput)); - ceres::internal::FixedArray<int> const fixed(items.begin(), items.end()); - EXPECT_THAT(fixed, testing::ElementsAreArray(kInput)); -} - -TEST(InitListConstructorTest, InitListConstruction) { - ceres::internal::FixedArray<int> fixed = {1, 2, 3}; - EXPECT_THAT(fixed, testing::ElementsAreArray({1, 2, 3})); -} - -TEST(FillConstructorTest, NonEmptyArrays) { - ceres::internal::FixedArray<int> stack_array(4, 1); - EXPECT_THAT(stack_array, testing::ElementsAreArray({1, 1, 1, 1})); - - ceres::internal::FixedArray<int, 0> heap_array(4, 1); - EXPECT_THAT(stack_array, testing::ElementsAreArray({1, 1, 1, 1})); -} - -TEST(FillConstructorTest, EmptyArray) { - ceres::internal::FixedArray<int> empty_fill(0, 1); - ceres::internal::FixedArray<int> empty_size(0); - EXPECT_EQ(empty_fill, empty_size); -} - -TEST(FillConstructorTest, NotTriviallyCopyable) { - std::string str = "abcd"; - ceres::internal::FixedArray<std::string> strings = {str, str, str, str}; - - ceres::internal::FixedArray<std::string> array(4, str); - EXPECT_EQ(array, strings); -} - -TEST(FillConstructorTest, Disambiguation) { - ceres::internal::FixedArray<size_t> a(1, 2); - EXPECT_THAT(a, testing::ElementsAre(2)); -} - -TEST(FixedArrayTest, ManySizedArrays) { - std::vector<int> sizes; - for (int i = 1; i < 100; i++) sizes.push_back(i); - for (int i = 100; i <= 1000; i += 100) sizes.push_back(i); - for (int n : sizes) { - TestArray<0>(n); - TestArray<1>(n); - TestArray<64>(n); - TestArray<1000>(n); - } -} - -TEST(FixedArrayTest, ManySizedArraysOfArraysOf1) { - for (int n = 1; n < 1000; n++) { - ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 0>(n))); - ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 1>(n))); - ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 64>(n))); - ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 1000>(n))); - } -} - -TEST(FixedArrayTest, ManySizedArraysOfArraysOf2) { - for (int n = 1; n < 1000; n++) { - TestArrayOfArrays<2, 0>(n); - TestArrayOfArrays<2, 1>(n); - TestArrayOfArrays<2, 64>(n); - TestArrayOfArrays<2, 1000>(n); - } -} - -// If value_type is put inside of a struct container, -// we might evoke this error in a hardened build unless data() is carefully -// written, so check on that. -// error: call to int __builtin___sprintf_chk(etc...) -// will always overflow destination buffer [-Werror] -TEST(FixedArrayTest, AvoidParanoidDiagnostics) { - ceres::internal::FixedArray<char, 32> buf(32); - snprintf(buf.data(), 32, "foo"); -} - -TEST(FixedArrayTest, TooBigInlinedSpace) { - struct TooBig { - char c[1 << 20]; - }; // too big for even one on the stack - - // Simulate the data members of ceres::internal::FixedArray, a pointer and a - // size_t. - struct Data { - std::tuple<size_t, std::allocator<double>> size_alloc_; - TooBig* p; - }; - - // Make sure TooBig objects are not inlined for 0 or default size. - static_assert( - sizeof(ceres::internal::FixedArray<TooBig, 0>) == sizeof(Data), - "0-sized ceres::internal::FixedArray should have same size as Data."); - static_assert( - alignof(ceres::internal::FixedArray<TooBig, 0>) == alignof(Data), - "0-sized ceres::internal::FixedArray should have same alignment as " - "Data."); - static_assert(sizeof(ceres::internal::FixedArray<TooBig>) == sizeof(Data), - "default-sized ceres::internal::FixedArray should have same " - "size as Data"); - static_assert(alignof(ceres::internal::FixedArray<TooBig>) == alignof(Data), - "default-sized ceres::internal::FixedArray should have same " - "alignment as Data."); -} - -// PickyDelete EXPECTs its class-scope deallocation funcs are unused. -struct PickyDelete { - void operator delete(void* p) { - EXPECT_TRUE(false) << __FUNCTION__; - ::operator delete(p); - } - void operator delete[](void* p) { - EXPECT_TRUE(false) << __FUNCTION__; - ::operator delete[](p); - } -}; - -TEST(FixedArrayTest, UsesGlobalAlloc) { - ceres::internal::FixedArray<PickyDelete, 0> a(5); -} - -TEST(FixedArrayTest, Data) { - static const int kInput[] = {2, 3, 5, 7, 11, 13, 17}; - ceres::internal::FixedArray<int> fa(std::begin(kInput), std::end(kInput)); - EXPECT_EQ(fa.data(), &*fa.begin()); - EXPECT_EQ(fa.data(), &fa[0]); - - const ceres::internal::FixedArray<int>& cfa = fa; - EXPECT_EQ(cfa.data(), &*cfa.begin()); - EXPECT_EQ(cfa.data(), &cfa[0]); -} - -TEST(FixedArrayTest, Empty) { - ceres::internal::FixedArray<int> empty(0); - ceres::internal::FixedArray<int> inline_filled(1); - ceres::internal::FixedArray<int, 0> heap_filled(1); - EXPECT_TRUE(empty.empty()); - EXPECT_FALSE(inline_filled.empty()); - EXPECT_FALSE(heap_filled.empty()); -} - -TEST(FixedArrayTest, FrontAndBack) { - ceres::internal::FixedArray<int, 3 * sizeof(int)> inlined = {1, 2, 3}; - EXPECT_EQ(inlined.front(), 1); - EXPECT_EQ(inlined.back(), 3); - - ceres::internal::FixedArray<int, 0> allocated = {1, 2, 3}; - EXPECT_EQ(allocated.front(), 1); - EXPECT_EQ(allocated.back(), 3); - - ceres::internal::FixedArray<int> one_element = {1}; - EXPECT_EQ(one_element.front(), one_element.back()); -} - -TEST(FixedArrayTest, ReverseIteratorInlined) { - ceres::internal::FixedArray<int, 5 * sizeof(int)> a = {0, 1, 2, 3, 4}; - - int counter = 5; - for (ceres::internal::FixedArray<int>::reverse_iterator iter = a.rbegin(); - iter != a.rend(); - ++iter) { - counter--; - EXPECT_EQ(counter, *iter); - } - EXPECT_EQ(counter, 0); - - counter = 5; - for (ceres::internal::FixedArray<int>::const_reverse_iterator iter = - a.rbegin(); - iter != a.rend(); - ++iter) { - counter--; - EXPECT_EQ(counter, *iter); - } - EXPECT_EQ(counter, 0); - - counter = 5; - for (auto iter = a.crbegin(); iter != a.crend(); ++iter) { - counter--; - EXPECT_EQ(counter, *iter); - } - EXPECT_EQ(counter, 0); -} - -TEST(FixedArrayTest, ReverseIteratorAllocated) { - ceres::internal::FixedArray<int, 0> a = {0, 1, 2, 3, 4}; - - int counter = 5; - for (ceres::internal::FixedArray<int>::reverse_iterator iter = a.rbegin(); - iter != a.rend(); - ++iter) { - counter--; - EXPECT_EQ(counter, *iter); - } - EXPECT_EQ(counter, 0); - - counter = 5; - for (ceres::internal::FixedArray<int>::const_reverse_iterator iter = - a.rbegin(); - iter != a.rend(); - ++iter) { - counter--; - EXPECT_EQ(counter, *iter); - } - EXPECT_EQ(counter, 0); - - counter = 5; - for (auto iter = a.crbegin(); iter != a.crend(); ++iter) { - counter--; - EXPECT_EQ(counter, *iter); - } - EXPECT_EQ(counter, 0); -} - -TEST(FixedArrayTest, Fill) { - ceres::internal::FixedArray<int, 5 * sizeof(int)> inlined(5); - int fill_val = 42; - inlined.fill(fill_val); - for (int i : inlined) EXPECT_EQ(i, fill_val); - - ceres::internal::FixedArray<int, 0> allocated(5); - allocated.fill(fill_val); - for (int i : allocated) EXPECT_EQ(i, fill_val); - - // It doesn't do anything, just make sure this compiles. - ceres::internal::FixedArray<int> empty(0); - empty.fill(fill_val); -} - -// TODO(johnsoncj): Investigate InlinedStorage default initialization in GCC 4.x -#ifndef __GNUC__ -TEST(FixedArrayTest, DefaultCtorDoesNotValueInit) { - using T = char; - constexpr auto capacity = 10; - using FixedArrType = ceres::internal::FixedArray<T, capacity>; - using FixedArrBuffType = - typename std::aligned_storage<sizeof(FixedArrType), - alignof(FixedArrType)>::type; - constexpr auto scrubbed_bits = 0x95; - constexpr auto length = capacity / 2; - - FixedArrBuffType buff; - std::memset(std::addressof(buff), scrubbed_bits, sizeof(FixedArrBuffType)); - - FixedArrType* arr = - ::new (static_cast<void*>(std::addressof(buff))) FixedArrType(length); - EXPECT_THAT(*arr, testing::Each(scrubbed_bits)); - arr->~FixedArrType(); -} -#endif // __GNUC__ - -// This is a stateful allocator, but the state lives outside of the -// allocator (in whatever test is using the allocator). This is odd -// but helps in tests where the allocator is propagated into nested -// containers - that chain of allocators uses the same state and is -// thus easier to query for aggregate allocation information. -template <typename T> -class CountingAllocator : public std::allocator<T> { - public: - using Alloc = std::allocator<T>; - using size_type = typename Alloc::size_type; - - CountingAllocator() = default; - explicit CountingAllocator(int64_t* b) : bytes_used_(b) {} - CountingAllocator(int64_t* b, int64_t* a) - : bytes_used_(b), instance_count_(a) {} - - template <typename U> - explicit CountingAllocator(const CountingAllocator<U>& x) - : Alloc(x), - bytes_used_(x.bytes_used_), - instance_count_(x.instance_count_) {} - - T* allocate(size_type n) { - assert(bytes_used_ != nullptr); - *bytes_used_ += n * sizeof(T); - return Alloc::allocate(n); - } - - void deallocate(T* p, size_type n) { - Alloc::deallocate(p, n); - assert(bytes_used_ != nullptr); - *bytes_used_ -= n * sizeof(T); - } - - int64_t* bytes_used_{nullptr}; - int64_t* instance_count_{nullptr}; -}; - -TEST(AllocatorSupportTest, CountInlineAllocations) { - constexpr size_t inlined_size = 4; - using Alloc = CountingAllocator<int>; - using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>; - - int64_t allocated = 0; - int64_t active_instances = 0; - - { - const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7}; - - Alloc alloc(&allocated, &active_instances); - - AllocFxdArr arr(ia, ia + inlined_size, alloc); - static_cast<void>(arr); - } - - EXPECT_EQ(allocated, 0); - EXPECT_EQ(active_instances, 0); -} - -TEST(AllocatorSupportTest, CountOutoflineAllocations) { - constexpr size_t inlined_size = 4; - using Alloc = CountingAllocator<int>; - using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>; - - int64_t allocated = 0; - int64_t active_instances = 0; - - { - const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7}; - Alloc alloc(&allocated, &active_instances); - - AllocFxdArr arr(ia, ia + CERES_INTERNAL_ARRAYSIZE(ia), alloc); - - EXPECT_EQ(allocated, arr.size() * sizeof(int)); - static_cast<void>(arr); - } - - EXPECT_EQ(active_instances, 0); -} - -TEST(AllocatorSupportTest, CountCopyInlineAllocations) { - constexpr size_t inlined_size = 4; - using Alloc = CountingAllocator<int>; - using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>; - - int64_t allocated1 = 0; - int64_t allocated2 = 0; - int64_t active_instances = 0; - Alloc alloc(&allocated1, &active_instances); - Alloc alloc2(&allocated2, &active_instances); - - { - int initial_value = 1; - - AllocFxdArr arr1(inlined_size / 2, initial_value, alloc); - - EXPECT_EQ(allocated1, 0); - - AllocFxdArr arr2(arr1, alloc2); - - EXPECT_EQ(allocated2, 0); - static_cast<void>(arr1); - static_cast<void>(arr2); - } - - EXPECT_EQ(active_instances, 0); -} - -TEST(AllocatorSupportTest, CountCopyOutoflineAllocations) { - constexpr size_t inlined_size = 4; - using Alloc = CountingAllocator<int>; - using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>; - - int64_t allocated1 = 0; - int64_t allocated2 = 0; - int64_t active_instances = 0; - Alloc alloc(&allocated1, &active_instances); - Alloc alloc2(&allocated2, &active_instances); - - { - int initial_value = 1; - - AllocFxdArr arr1(inlined_size * 2, initial_value, alloc); - - EXPECT_EQ(allocated1, arr1.size() * sizeof(int)); - - AllocFxdArr arr2(arr1, alloc2); - - EXPECT_EQ(allocated2, inlined_size * 2 * sizeof(int)); - static_cast<void>(arr1); - static_cast<void>(arr2); - } - - EXPECT_EQ(active_instances, 0); -} - -TEST(AllocatorSupportTest, SizeValAllocConstructor) { - using testing::AllOf; - using testing::Each; - using testing::SizeIs; - - constexpr size_t inlined_size = 4; - using Alloc = CountingAllocator<int>; - using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>; - - { - auto len = inlined_size / 2; - auto val = 0; - int64_t allocated = 0; - AllocFxdArr arr(len, val, Alloc(&allocated)); - - EXPECT_EQ(allocated, 0); - EXPECT_THAT(arr, AllOf(SizeIs(len), Each(0))); - } - - { - auto len = inlined_size * 2; - auto val = 0; - int64_t allocated = 0; - AllocFxdArr arr(len, val, Alloc(&allocated)); - - EXPECT_EQ(allocated, len * sizeof(int)); - EXPECT_THAT(arr, AllOf(SizeIs(len), Each(0))); - } -} - -struct EigenStruct { - Eigen::Vector4d data; -}; - -static_assert( - std::is_same<ceres::internal::FixedArrayDefaultAllocator<double>, - std::allocator<double>>::value, - "Double is a trivial type, so std::allocator should be used here."); -static_assert( - std::is_same<ceres::internal::FixedArrayDefaultAllocator<double*>, - std::allocator<double*>>::value, - "A pointer is a trivial type, so std::allocator should be used here."); -static_assert( - std::is_same<ceres::internal::FixedArrayDefaultAllocator<Eigen::Matrix4d>, - Eigen::aligned_allocator<Eigen::Matrix4d>>::value, - "An Eigen::Matrix4d needs the Eigen::aligned_allocator for proper " - "alignment."); -static_assert( - std::is_same<ceres::internal::FixedArrayDefaultAllocator<EigenStruct>, - Eigen::aligned_allocator<EigenStruct>>::value, - "A struct containing fixed size Eigen types needs Eigen::aligned_allocator " - "for proper alignment."); - -} // namespace
diff --git a/internal/ceres/gradient_checker_test.cc b/internal/ceres/gradient_checker_test.cc index 5d2ea14..0b9f3a6 100644 --- a/internal/ceres/gradient_checker_test.cc +++ b/internal/ceres/gradient_checker_test.cc
@@ -37,6 +37,7 @@ #include <utility> #include <vector> +#include "absl/container/fixed_array.h" #include "absl/log/check.h" #include "absl/log/log.h" #include "ceres/cost_function.h" @@ -206,7 +207,7 @@ parameter_sizes[2] = 4; // Make a random set of blocks. - FixedArray<double*> parameters(num_parameters); + absl::FixedArray<double*> parameters(num_parameters); std::mt19937 prng; std::uniform_real_distribution<double> distribution(-1.0, 1.0); auto randu = [&prng, &distribution] { return distribution(prng); };
diff --git a/internal/ceres/manifold.cc b/internal/ceres/manifold.cc index bfb2a22..6bd320a 100644 --- a/internal/ceres/manifold.cc +++ b/internal/ceres/manifold.cc
@@ -5,7 +5,6 @@ #include "absl/log/check.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/fixed_array.h" namespace ceres { namespace {
diff --git a/internal/ceres/problem_impl.cc b/internal/ceres/problem_impl.cc index eafbb20..cc86d2c 100644 --- a/internal/ceres/problem_impl.cc +++ b/internal/ceres/problem_impl.cc
@@ -41,6 +41,7 @@ #include <utility> #include <vector> +#include "absl/container/fixed_array.h" #include "absl/log/check.h" #include "absl/log/log.h" #include "ceres/casts.h" @@ -52,7 +53,6 @@ #include "ceres/evaluation_callback.h" #include "ceres/evaluator.h" #include "ceres/internal/export.h" -#include "ceres/internal/fixed_array.h" #include "ceres/loss_function.h" #include "ceres/manifold.h" #include "ceres/map_util.h" @@ -776,7 +776,7 @@ } double dummy_cost = 0.0; - FixedArray<double, 32> scratch( + absl::FixedArray<double> scratch( residual_block->NumScratchDoublesForEvaluate()); return residual_block->Evaluate(apply_loss_function, cost ? cost : &dummy_cost,
diff --git a/internal/ceres/residual_block.cc b/internal/ceres/residual_block.cc index da6b88e..aeb761f 100644 --- a/internal/ceres/residual_block.cc +++ b/internal/ceres/residual_block.cc
@@ -35,12 +35,12 @@ #include <cstddef> #include <vector> +#include "absl/container/fixed_array.h" #include "absl/log/check.h" #include "absl/log/log.h" #include "ceres/corrector.h" #include "ceres/cost_function.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/fixed_array.h" #include "ceres/loss_function.h" #include "ceres/manifold.h" #include "ceres/parameter_block.h" @@ -77,13 +77,13 @@ // Collect the parameters from their blocks. This will rarely allocate, since // residuals taking more than 8 parameter block arguments are rare. - FixedArray<const double*, 8> parameters(num_parameter_blocks); + absl::FixedArray<const double*> parameters(num_parameter_blocks); for (int i = 0; i < num_parameter_blocks; ++i) { parameters[i] = parameter_blocks_[i]->state(); } // Put pointers into the scratch space into global_jacobians as appropriate. - FixedArray<double*, 8> global_jacobians(num_parameter_blocks); + absl::FixedArray<double*> global_jacobians(num_parameter_blocks); if (jacobians != nullptr) { for (int i = 0; i < num_parameter_blocks; ++i) { const ParameterBlock* parameter_block = parameter_blocks_[i];
diff --git a/internal/ceres/schur_eliminator_impl.h b/internal/ceres/schur_eliminator_impl.h index 545f906..6c287fd 100644 --- a/internal/ceres/schur_eliminator_impl.h +++ b/internal/ceres/schur_eliminator_impl.h
@@ -54,12 +54,12 @@ #include <map> #include "Eigen/Dense" +#include "absl/container/fixed_array.h" #include "absl/log/check.h" #include "ceres/block_random_access_matrix.h" #include "ceres/block_sparse_matrix.h" #include "ceres/block_structure.h" #include "ceres/internal/eigen.h" -#include "ceres/internal/fixed_array.h" #include "ceres/invert_psd_matrix.h" #include "ceres/map_util.h" #include "ceres/parallel_for.h" @@ -249,7 +249,7 @@ ete.setZero(); } - FixedArray<double, 8> g(e_block_size); + absl::FixedArray<double> g(e_block_size); typename EigenTypes<kEBlockSize>::VectorRef gref(g.data(), e_block_size); gref.setZero(); @@ -283,7 +283,7 @@ // rhs = F'b - F'E(E'E)^(-1) E'b if (rhs) { - FixedArray<double, 8> inverse_ete_g(e_block_size); + absl::FixedArray<double> inverse_ete_g(e_block_size); MatrixVectorMultiply<kEBlockSize, kEBlockSize, 0>( inverse_ete.data(), e_block_size, @@ -336,7 +336,7 @@ const Cell& e_cell = row.cells.front(); DCHECK_EQ(e_block_id, e_cell.block_id); - FixedArray<double, 8> sj(row.block.size); + absl::FixedArray<double> sj(row.block.size); typename EigenTypes<kRowBlockSize>::VectorRef(sj.data(), row.block.size) = typename EigenTypes<kRowBlockSize>::ConstVectorRef(