Allow to store pointers in ProductManifold
Change-Id: I32df7afab3a195efb0407b0d8f35dcd2d7cb95d2
diff --git a/docs/source/nnls_modeling.rst b/docs/source/nnls_modeling.rst
index 51afcec..c6b7987 100644
--- a/docs/source/nnls_modeling.rst
+++ b/docs/source/nnls_modeling.rst
@@ -1548,6 +1548,13 @@
ProductManifold<SubsetManifold, SubsetManifold> manifold(manifold1,
manifold2);
+In advanced use cases, manifolds can be dynamically allocated and passed as (smart) pointers:
+
+.. code-block:: c++
+
+ ProductManifold<std::unique_ptr<QuaternionManifold>, EuclideanManifold<3>> se3
+ {std::make_unique<QuaternionManifold>(), EuclideanManifold<3>{}};
+
In C++17, the template parameters can be left out as they are automatically
deduced making the initialization much simpler:
diff --git a/include/ceres/product_manifold.h b/include/ceres/product_manifold.h
index b7ebe4d..33f046d 100644
--- a/include/ceres/product_manifold.h
+++ b/include/ceres/product_manifold.h
@@ -35,6 +35,7 @@
#include <algorithm>
#include <array>
+#include <cassert>
#include <cstddef>
#include <numeric>
#include <tuple>
@@ -67,6 +68,12 @@
// ProductManifold<SubsetManifold, SubsetManifold> manifold(manifold1,
// manifold2);
//
+// In advanced use cases, manifolds can be dynamically allocated and passed as
+// (smart) pointers:
+//
+// ProductManifold<std::unique_ptr<QuaternionManifold>, EuclideanManifold<3>>
+// se3{std::make_unique<QuaternionManifold>(), EuclideanManifold<3>{}};
+//
// In C++17, the template parameters can be left out as they are automatically
// deduced making the initialization much simpler:
//
@@ -131,11 +138,13 @@
template <std::size_t... Indices, typename... Args>
explicit ProductManifold(std::index_sequence<Indices...>, Args&&... manifolds)
: manifolds_{std::forward<Args>(manifolds)...},
- buffer_size_{
- (std::max)({(std::get<Indices>(manifolds_).TangentSize() *
- std::get<Indices>(manifolds_).AmbientSize())...})},
- ambient_sizes_{std::get<Indices>(manifolds_).AmbientSize()...},
- tangent_sizes_{std::get<Indices>(manifolds_).TangentSize()...},
+ buffer_size_{(std::max)(
+ {(Dereference(std::get<Indices>(manifolds_)).TangentSize() *
+ Dereference(std::get<Indices>(manifolds_)).AmbientSize())...})},
+ ambient_sizes_{
+ Dereference(std::get<Indices>(manifolds_)).AmbientSize()...},
+ tangent_sizes_{
+ Dereference(std::get<Indices>(manifolds_)).TangentSize()...},
ambient_offsets_{ExclusiveScan(ambient_sizes_)},
tangent_offsets_{ExclusiveScan(tangent_sizes_)},
ambient_size_{
@@ -148,7 +157,7 @@
const double* delta,
double* x_plus_delta,
std::index_sequence<Index0, Indices...>) const {
- if (!std::get<Index0>(manifolds_)
+ if (!Dereference(std::get<Index0>(manifolds_))
.Plus(x + ambient_offsets_[Index0],
delta + tangent_offsets_[Index0],
x_plus_delta + ambient_offsets_[Index0])) {
@@ -170,7 +179,7 @@
const double* x,
double* y_minus_x,
std::index_sequence<Index0, Indices...>) const {
- if (!std::get<Index0>(manifolds_)
+ if (!Dereference(std::get<Index0>(manifolds_))
.Minus(y + ambient_offsets_[Index0],
x + ambient_offsets_[Index0],
y_minus_x + tangent_offsets_[Index0])) {
@@ -192,7 +201,7 @@
MatrixRef& jacobian,
internal::FixedArray<double>& buffer,
std::index_sequence<Index0, Indices...>) const {
- if (!std::get<Index0>(manifolds_)
+ if (!Dereference(std::get<Index0>(manifolds_))
.PlusJacobian(x + ambient_offsets_[Index0], buffer.data())) {
return false;
}
@@ -221,7 +230,7 @@
MatrixRef& jacobian,
internal::FixedArray<double>& buffer,
std::index_sequence<Index0, Indices...>) const {
- if (!std::get<Index0>(manifolds_)
+ if (!Dereference(std::get<Index0>(manifolds_))
.MinusJacobian(x + ambient_offsets_[Index0], buffer.data())) {
return false;
}
@@ -259,6 +268,39 @@
return result;
}
+ // TODO Replace by std::void_t once C++17 is available
+ template <typename... Types>
+ struct Void {
+ using type = void;
+ };
+
+ template <typename T, typename E = void>
+ struct IsDereferenceable : std::false_type {};
+
+ template <typename T>
+ struct IsDereferenceable<T, typename Void<decltype(*std::declval<T>())>::type>
+ : std::true_type {};
+
+ template <typename T,
+ std::enable_if_t<!IsDereferenceable<T>::value>* = nullptr>
+ static constexpr decltype(auto) Dereference(T& value) {
+ return value;
+ }
+
+ // Support dereferenceable types such as std::unique_ptr, std::shared_ptr, raw
+ // pointers etc.
+ template <typename T,
+ std::enable_if_t<IsDereferenceable<T>::value>* = nullptr>
+ static constexpr decltype(auto) Dereference(T& value) {
+ return *value;
+ }
+
+ template <typename T>
+ static constexpr decltype(auto) Dereference(T* p) {
+ assert(p != nullptr);
+ return *p;
+ }
+
std::tuple<Manifold0, Manifold1, ManifoldN...> manifolds_;
int buffer_size_;
std::array<int, kNumManifolds> ambient_sizes_;
diff --git a/internal/ceres/manifold_test.cc b/internal/ceres/manifold_test.cc
index 6210c7a..46cd700 100644
--- a/internal/ceres/manifold_test.cc
+++ b/internal/ceres/manifold_test.cc
@@ -479,6 +479,24 @@
EXPECT_EQ(manifold1.TangentSize(), manifold2.TangentSize());
}
+TEST(ProductManifold, Pointers) {
+ auto p = std::make_unique<QuaternionManifold>();
+ auto q = std::make_shared<EuclideanManifold<3>>();
+
+ ProductManifold<std::unique_ptr<Manifold>,
+ EuclideanManifold<3>,
+ std::shared_ptr<EuclideanManifold<3>>>
+ manifold1{
+ std::make_unique<QuaternionManifold>(), EuclideanManifold<3>{}, q};
+ ProductManifold<QuaternionManifold*,
+ EuclideanManifold<3>,
+ std::shared_ptr<EuclideanManifold<3>>>
+ manifold2{p.get(), EuclideanManifold<3>{}, q};
+
+ EXPECT_EQ(manifold1.AmbientSize(), manifold2.AmbientSize());
+ EXPECT_EQ(manifold1.TangentSize(), manifold2.TangentSize());
+}
+
TEST(QuaternionManifold, PlusPiBy2) {
QuaternionManifold manifold;
Vector x = Vector::Zero(4);