mirror of
https://github.com/ceres-solver/ceres-solver.git
synced 2026-08-29 08:34:37 +08:00
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
This commit is contained in:
@@ -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 @@ class AutoDiffFirstOrderFunction final : public FirstOrderFunction {
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -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 @@ class DynamicAutoDiffCostFunction final : public DynamicCostFunction {
|
||||
|
||||
// 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
|
||||
|
||||
@@ -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 @@ class CERES_EXPORT DynamicCostFunctionToFunctor {
|
||||
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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 @@ namespace ceres::internal {
|
||||
// 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 @@ struct ArraySelector<T,
|
||||
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>
|
||||
|
||||
@@ -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_
|
||||
@@ -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 @@ struct NumericDiff {
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ class NumericDiffCostFunction final
|
||||
bool Evaluate(double const* const* parameters,
|
||||
double* residuals,
|
||||
double** jacobians) const override {
|
||||
using internal::FixedArray;
|
||||
using absl::FixedArray;
|
||||
using internal::NumericDiff;
|
||||
|
||||
using ParameterDims =
|
||||
|
||||
@@ -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 @@ class NumericDiffFirstOrderFunction final : public FirstOrderFunction {
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
@@ -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 @@ class ProductManifold final : public Manifold {
|
||||
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 @@ class ProductManifold final : public Manifold {
|
||||
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 @@ class ProductManifold final : public Manifold {
|
||||
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 @@ class ProductManifold final : public Manifold {
|
||||
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 @@ class ProductManifold final : public Manifold {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ set(Threads_DEPENDENCY "find_dependency (Threads)" PARENT_SCOPE)
|
||||
|
||||
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 @@ if (BUILD_TESTING)
|
||||
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)
|
||||
|
||||
@@ -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 @@ namespace ceres::internal {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 @@ TEST(AutoDiff, AlignedAllocationTest) {
|
||||
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);
|
||||
|
||||
@@ -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 @@ template <typename Derived>
|
||||
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 @@ inline double Dot(const VectorLikeX& x,
|
||||
const VectorLikeY& y,
|
||||
ContextImpl* context,
|
||||
int num_threads) {
|
||||
FixedArray<double> dots(num_threads, 0.);
|
||||
absl::FixedArray<double> dots(num_threads, 0.);
|
||||
ParallelFor(
|
||||
context,
|
||||
0,
|
||||
|
||||
@@ -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
|
||||
@@ -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 @@ TEST(GradientChecker, SmokeTest) {
|
||||
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); };
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include "absl/log/check.h"
|
||||
#include "ceres/internal/eigen.h"
|
||||
#include "ceres/internal/fixed_array.h"
|
||||
|
||||
namespace ceres {
|
||||
namespace {
|
||||
|
||||
@@ -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 @@ bool ProblemImpl::EvaluateResidualBlock(ResidualBlock* residual_block,
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
@@ -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 @@ bool ResidualBlock::Evaluate(const bool apply_loss_function,
|
||||
|
||||
// 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];
|
||||
|
||||
@@ -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 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::Eliminate(
|
||||
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 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::Eliminate(
|
||||
// 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 @@ void SchurEliminator<kRowBlockSize, kEBlockSize, kFBlockSize>::BackSubstitute(
|
||||
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(
|
||||
|
||||
Reference in New Issue
Block a user