mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-11 08:03:55 +00:00
fab1eebe2c
* First draft of Eigen::Tensor support * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix build errors * Weird allocator stuff? * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Remove unused + additional allocator junk * Disable warning * Use constexpr * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * clang tidy fixes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Resolve comments * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Remove auto constexpr function * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Try again for older C++ * Oops forgot constexpr * Move to new files as suggested * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix weird tests * Fix nits * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Oops, forgot import * Fix clang 3.6 bug * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * More comprehensive test suite * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Refactor allocators to make things more clear * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Switch to std::copy * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Switch to DSizes instead of array * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Address feedback * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix python + dummy c++ change to trigger build * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Alignment * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add include guard * Forgot inline * Fix compiler warning * Remove bad test * Better type signatures * Add guards to make compiler requirements more explicit * style: pre-commit fixes * Force rerun of tests due to flake * style: pre-commit fixes * Keep pragmas & all related comments together, add PLEASE KEEP IN SYNC * Move headers out of detail * style: pre-commit fixes * Fix cmake * Improve casting * style: pre-commit fixes * Add a ton more tests + refactor * Improve names * style: pre-commit fixes * Update include/pybind11/eigen/tensor.h Co-authored-by: Aaron Gokaslan <skylion.aaron@gmail.com> * Fix tests * style: pre-commit fixes * Update * Add a test to verify that strange numpy arrays work * Fix dumb compiler warning * Better tests * Better tests * Fix tests * style: pre-commit fixes * More test fixes * style: pre-commit fixes * A ton more test coverage * Fix tests * style: pre-commit fixes * style: pre-commit fixes * Add back constexpr * Another test * style: pre-commit fixes * Improve tests * Whoops * Less magic numbers * Update tests/test_eigen_tensor.py Co-authored-by: Sergiu Deitsch <sergiud@users.noreply.github.com> * Update tests/test_eigen_tensor.py Co-authored-by: Sergiu Deitsch <sergiud@users.noreply.github.com> * style: pre-commit fixes * Fix tests * style: pre-commit fixes * Fix memory leak * style: pre-commit fixes * Fix order * style: pre-commit fixes * Add test to make sure unsafe casts fail * Minor bug fix to work on 32 bit machines * Implement convert flag * style: pre-commit fixes * Switch to correct TensorMap const use * style: pre-commit fixes * Support older versions of eigen * Weird c++ compilers * Fix Eigen bug * Fix another eigen bug * Yet another eigen bug * Potential flakes? * style: pre-commit fixes * Rerun tests with dummy exception to find out what is going on * Another dummy test run * Ablate more * Found the broken test? * One step closer * one step further * Double check * one thing at a time * Give up and disable the test * Clang lies about being gcc * Oops, fix matrix test * style: pre-commit fixes * Add tests to verify scalar conversions * style: pre-commit fixes * Fix nits * Support no_array * Fix tests * style: pre-commit fixes * Silence compiler warning * Improve build system for ancient compilers * Make clang happy * Make gcc happy * Implement Skylion's suggestions * Fix warning * Inline const pointer check * Implement suggestions * style: pre-commit fixes * Improve tests * Typo * style: pre-commit fixes * Support Google's build environment * style: pre-commit fixes * Update include/pybind11/eigen/tensor.h Co-authored-by: Aaron Gokaslan <skylion.aaron@gmail.com> * style: pre-commit fixes * Test cleanup per Skylion * Switch to remvove_cv_t * Cleaner test * style: pre-commit fixes * Remove tensor from eigen.h, update tests * style: pre-commit fixes Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Ralf W. Grosse-Kunstleve <rwgk@google.com> Co-authored-by: Aaron Gokaslan <aaronGokaslan@gmail.com> Co-authored-by: Aaron Gokaslan <skylion.aaron@gmail.com> Co-authored-by: Sergiu Deitsch <sergiud@users.noreply.github.com>
519 lines
18 KiB
C++
519 lines
18 KiB
C++
/*
|
|
pybind11/eigen/tensor.h: Transparent conversion for Eigen tensors
|
|
|
|
All rights reserved. Use of this source code is governed by a
|
|
BSD-style license that can be found in the LICENSE file.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "../numpy.h"
|
|
|
|
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
|
|
static_assert(__GNUC__ > 5, "Eigen Tensor support in pybind11 requires GCC > 5.0");
|
|
#endif
|
|
|
|
#if defined(_MSC_VER)
|
|
# pragma warning(push)
|
|
# pragma warning(disable : 4554) // Tensor.h warning
|
|
# pragma warning(disable : 4127) // Tensor.h warning
|
|
#elif defined(__MINGW32__)
|
|
# pragma GCC diagnostic push
|
|
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
|
#endif
|
|
|
|
#include <unsupported/Eigen/CXX11/Tensor>
|
|
|
|
#if defined(_MSC_VER)
|
|
# pragma warning(pop)
|
|
#elif defined(__MINGW32__)
|
|
# pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
static_assert(EIGEN_VERSION_AT_LEAST(3, 3, 0),
|
|
"Eigen Tensor support in pybind11 requires Eigen >= 3.3.0");
|
|
|
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
|
|
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
|
|
|
inline bool is_tensor_aligned(const void *data) {
|
|
return (reinterpret_cast<std::size_t>(data) % EIGEN_DEFAULT_ALIGN_BYTES) == 0;
|
|
}
|
|
|
|
template <typename T>
|
|
constexpr int compute_array_flag_from_tensor() {
|
|
static_assert((static_cast<int>(T::Layout) == static_cast<int>(Eigen::RowMajor))
|
|
|| (static_cast<int>(T::Layout) == static_cast<int>(Eigen::ColMajor)),
|
|
"Layout must be row or column major");
|
|
return (static_cast<int>(T::Layout) == static_cast<int>(Eigen::RowMajor)) ? array::c_style
|
|
: array::f_style;
|
|
}
|
|
|
|
template <typename T>
|
|
struct eigen_tensor_helper {};
|
|
|
|
template <typename Scalar_, int NumIndices_, int Options_, typename IndexType>
|
|
struct eigen_tensor_helper<Eigen::Tensor<Scalar_, NumIndices_, Options_, IndexType>> {
|
|
using Type = Eigen::Tensor<Scalar_, NumIndices_, Options_, IndexType>;
|
|
using ValidType = void;
|
|
|
|
static Eigen::DSizes<typename Type::Index, Type::NumIndices> get_shape(const Type &f) {
|
|
return f.dimensions();
|
|
}
|
|
|
|
static constexpr bool
|
|
is_correct_shape(const Eigen::DSizes<typename Type::Index, Type::NumIndices> & /*shape*/) {
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
struct helper {};
|
|
|
|
template <size_t... Is>
|
|
struct helper<index_sequence<Is...>> {
|
|
static constexpr auto value = concat(const_name(((void) Is, "?"))...);
|
|
};
|
|
|
|
static constexpr auto dimensions_descriptor
|
|
= helper<decltype(make_index_sequence<Type::NumIndices>())>::value;
|
|
|
|
template <typename... Args>
|
|
static Type *alloc(Args &&...args) {
|
|
return new Type(std::forward<Args>(args)...);
|
|
}
|
|
|
|
static void free(Type *tensor) { delete tensor; }
|
|
};
|
|
|
|
template <typename Scalar_, typename std::ptrdiff_t... Indices, int Options_, typename IndexType>
|
|
struct eigen_tensor_helper<
|
|
Eigen::TensorFixedSize<Scalar_, Eigen::Sizes<Indices...>, Options_, IndexType>> {
|
|
using Type = Eigen::TensorFixedSize<Scalar_, Eigen::Sizes<Indices...>, Options_, IndexType>;
|
|
using ValidType = void;
|
|
|
|
static constexpr Eigen::DSizes<typename Type::Index, Type::NumIndices>
|
|
get_shape(const Type & /*f*/) {
|
|
return get_shape();
|
|
}
|
|
|
|
static constexpr Eigen::DSizes<typename Type::Index, Type::NumIndices> get_shape() {
|
|
return Eigen::DSizes<typename Type::Index, Type::NumIndices>(Indices...);
|
|
}
|
|
|
|
static bool
|
|
is_correct_shape(const Eigen::DSizes<typename Type::Index, Type::NumIndices> &shape) {
|
|
return get_shape() == shape;
|
|
}
|
|
|
|
static constexpr auto dimensions_descriptor = concat(const_name<Indices>()...);
|
|
|
|
template <typename... Args>
|
|
static Type *alloc(Args &&...args) {
|
|
Eigen::aligned_allocator<Type> allocator;
|
|
return ::new (allocator.allocate(1)) Type(std::forward<Args>(args)...);
|
|
}
|
|
|
|
static void free(Type *tensor) {
|
|
Eigen::aligned_allocator<Type> allocator;
|
|
tensor->~Type();
|
|
allocator.deallocate(tensor, 1);
|
|
}
|
|
};
|
|
|
|
template <typename Type, bool ShowDetails, bool NeedsWriteable = false>
|
|
struct get_tensor_descriptor {
|
|
static constexpr auto details
|
|
= const_name<NeedsWriteable>(", flags.writeable", "")
|
|
+ const_name<static_cast<int>(Type::Layout) == static_cast<int>(Eigen::RowMajor)>(
|
|
", flags.c_contiguous", ", flags.f_contiguous");
|
|
static constexpr auto value
|
|
= const_name("numpy.ndarray[") + npy_format_descriptor<typename Type::Scalar>::name
|
|
+ const_name("[") + eigen_tensor_helper<remove_cv_t<Type>>::dimensions_descriptor
|
|
+ const_name("]") + const_name<ShowDetails>(details, const_name("")) + const_name("]");
|
|
};
|
|
|
|
// When EIGEN_AVOID_STL_ARRAY is defined, Eigen::DSizes<T, 0> does not have the begin() member
|
|
// function. Falling back to a simple loop works around this issue.
|
|
//
|
|
// We need to disable the type-limits warning for the inner loop when size = 0.
|
|
|
|
#if defined(__GNUC__)
|
|
# pragma GCC diagnostic push
|
|
# pragma GCC diagnostic ignored "-Wtype-limits"
|
|
#endif
|
|
|
|
template <typename T, int size>
|
|
std::vector<T> convert_dsizes_to_vector(const Eigen::DSizes<T, size> &arr) {
|
|
std::vector<T> result(size);
|
|
|
|
for (size_t i = 0; i < size; i++) {
|
|
result[i] = arr[i];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename T, int size>
|
|
Eigen::DSizes<T, size> get_shape_for_array(const array &arr) {
|
|
Eigen::DSizes<T, size> result;
|
|
const T *shape = arr.shape();
|
|
for (size_t i = 0; i < size; i++) {
|
|
result[i] = shape[i];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#if defined(__GNUC__)
|
|
# pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
template <typename Type>
|
|
struct type_caster<Type, typename eigen_tensor_helper<Type>::ValidType> {
|
|
using Helper = eigen_tensor_helper<Type>;
|
|
static constexpr auto temp_name = get_tensor_descriptor<Type, false>::value;
|
|
PYBIND11_TYPE_CASTER(Type, temp_name);
|
|
|
|
bool load(handle src, bool convert) {
|
|
if (!convert) {
|
|
if (!isinstance<array>(src)) {
|
|
return false;
|
|
}
|
|
array temp = array::ensure(src);
|
|
if (!temp) {
|
|
return false;
|
|
}
|
|
|
|
if (!convert && !temp.dtype().is(dtype::of<typename Type::Scalar>())) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
array_t<typename Type::Scalar, compute_array_flag_from_tensor<Type>()> arr(
|
|
reinterpret_borrow<object>(src));
|
|
|
|
if (arr.ndim() != Type::NumIndices) {
|
|
return false;
|
|
}
|
|
auto shape = get_shape_for_array<typename Type::Index, Type::NumIndices>(arr);
|
|
|
|
if (!Helper::is_correct_shape(shape)) {
|
|
return false;
|
|
}
|
|
|
|
#if EIGEN_VERSION_AT_LEAST(3, 4, 0)
|
|
auto data_pointer = arr.data();
|
|
#else
|
|
// Handle Eigen bug
|
|
auto data_pointer = const_cast<typename Type::Scalar *>(arr.data());
|
|
#endif
|
|
|
|
if (is_tensor_aligned(arr.data())) {
|
|
value = Eigen::TensorMap<const Type, Eigen::Aligned>(data_pointer, shape);
|
|
} else {
|
|
value = Eigen::TensorMap<const Type>(data_pointer, shape);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static handle cast(Type &&src, return_value_policy policy, handle parent) {
|
|
if (policy == return_value_policy::reference
|
|
|| policy == return_value_policy::reference_internal) {
|
|
pybind11_fail("Cannot use a reference return value policy for an rvalue");
|
|
}
|
|
return cast_impl(&src, return_value_policy::move, parent);
|
|
}
|
|
|
|
static handle cast(const Type &&src, return_value_policy policy, handle parent) {
|
|
if (policy == return_value_policy::reference
|
|
|| policy == return_value_policy::reference_internal) {
|
|
pybind11_fail("Cannot use a reference return value policy for an rvalue");
|
|
}
|
|
return cast_impl(&src, return_value_policy::move, parent);
|
|
}
|
|
|
|
static handle cast(Type &src, return_value_policy policy, handle parent) {
|
|
if (policy == return_value_policy::automatic
|
|
|| policy == return_value_policy::automatic_reference) {
|
|
policy = return_value_policy::copy;
|
|
}
|
|
return cast_impl(&src, policy, parent);
|
|
}
|
|
|
|
static handle cast(const Type &src, return_value_policy policy, handle parent) {
|
|
if (policy == return_value_policy::automatic
|
|
|| policy == return_value_policy::automatic_reference) {
|
|
policy = return_value_policy::copy;
|
|
}
|
|
return cast(&src, policy, parent);
|
|
}
|
|
|
|
static handle cast(Type *src, return_value_policy policy, handle parent) {
|
|
if (policy == return_value_policy::automatic) {
|
|
policy = return_value_policy::take_ownership;
|
|
} else if (policy == return_value_policy::automatic_reference) {
|
|
policy = return_value_policy::reference;
|
|
}
|
|
return cast_impl(src, policy, parent);
|
|
}
|
|
|
|
static handle cast(const Type *src, return_value_policy policy, handle parent) {
|
|
if (policy == return_value_policy::automatic) {
|
|
policy = return_value_policy::take_ownership;
|
|
} else if (policy == return_value_policy::automatic_reference) {
|
|
policy = return_value_policy::reference;
|
|
}
|
|
return cast_impl(src, policy, parent);
|
|
}
|
|
|
|
template <typename C>
|
|
static handle cast_impl(C *src, return_value_policy policy, handle parent) {
|
|
object parent_object;
|
|
bool writeable = false;
|
|
switch (policy) {
|
|
case return_value_policy::move:
|
|
if (std::is_const<C>::value) {
|
|
pybind11_fail("Cannot move from a constant reference");
|
|
}
|
|
|
|
src = Helper::alloc(std::move(*src));
|
|
|
|
parent_object
|
|
= capsule(src, [](void *ptr) { Helper::free(reinterpret_cast<Type *>(ptr)); });
|
|
writeable = true;
|
|
break;
|
|
|
|
case return_value_policy::take_ownership:
|
|
if (std::is_const<C>::value) {
|
|
// This cast is ugly, and might be UB in some cases, but we don't have an
|
|
// alterantive here as we must free that memory
|
|
Helper::free(const_cast<Type *>(src));
|
|
pybind11_fail("Cannot take ownership of a const reference");
|
|
}
|
|
|
|
parent_object
|
|
= capsule(src, [](void *ptr) { Helper::free(reinterpret_cast<Type *>(ptr)); });
|
|
writeable = true;
|
|
break;
|
|
|
|
case return_value_policy::copy:
|
|
writeable = true;
|
|
break;
|
|
|
|
case return_value_policy::reference:
|
|
parent_object = none();
|
|
writeable = !std::is_const<C>::value;
|
|
break;
|
|
|
|
case return_value_policy::reference_internal:
|
|
// Default should do the right thing
|
|
if (!parent) {
|
|
pybind11_fail("Cannot use reference internal when there is no parent");
|
|
}
|
|
parent_object = reinterpret_borrow<object>(parent);
|
|
writeable = !std::is_const<C>::value;
|
|
break;
|
|
|
|
default:
|
|
pybind11_fail("pybind11 bug in eigen.h, please file a bug report");
|
|
}
|
|
|
|
auto result = array_t<typename Type::Scalar, compute_array_flag_from_tensor<Type>()>(
|
|
convert_dsizes_to_vector(Helper::get_shape(*src)), src->data(), parent_object);
|
|
|
|
if (!writeable) {
|
|
array_proxy(result.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
|
|
}
|
|
|
|
return result.release();
|
|
}
|
|
};
|
|
|
|
template <typename StoragePointerType,
|
|
bool needs_writeable,
|
|
enable_if_t<!needs_writeable, bool> = true>
|
|
StoragePointerType get_array_data_for_type(array &arr) {
|
|
#if EIGEN_VERSION_AT_LEAST(3, 4, 0)
|
|
return reinterpret_cast<StoragePointerType>(arr.data());
|
|
#else
|
|
// Handle Eigen bug
|
|
return reinterpret_cast<StoragePointerType>(const_cast<void *>(arr.data()));
|
|
#endif
|
|
}
|
|
|
|
template <typename StoragePointerType,
|
|
bool needs_writeable,
|
|
enable_if_t<needs_writeable, bool> = true>
|
|
StoragePointerType get_array_data_for_type(array &arr) {
|
|
return reinterpret_cast<StoragePointerType>(arr.mutable_data());
|
|
}
|
|
|
|
template <typename T, typename = void>
|
|
struct get_storage_pointer_type;
|
|
|
|
template <typename MapType>
|
|
struct get_storage_pointer_type<MapType, void_t<typename MapType::StoragePointerType>> {
|
|
using SPT = typename MapType::StoragePointerType;
|
|
};
|
|
|
|
template <typename MapType>
|
|
struct get_storage_pointer_type<MapType, void_t<typename MapType::PointerArgType>> {
|
|
using SPT = typename MapType::PointerArgType;
|
|
};
|
|
|
|
template <typename Type, int Options>
|
|
struct type_caster<Eigen::TensorMap<Type, Options>,
|
|
typename eigen_tensor_helper<remove_cv_t<Type>>::ValidType> {
|
|
using MapType = Eigen::TensorMap<Type, Options>;
|
|
using Helper = eigen_tensor_helper<remove_cv_t<Type>>;
|
|
|
|
bool load(handle src, bool /*convert*/) {
|
|
// Note that we have a lot more checks here as we want to make sure to avoid copies
|
|
if (!isinstance<array>(src)) {
|
|
return false;
|
|
}
|
|
auto arr = reinterpret_borrow<array>(src);
|
|
if ((arr.flags() & compute_array_flag_from_tensor<Type>()) == 0) {
|
|
return false;
|
|
}
|
|
|
|
if (!arr.dtype().is(dtype::of<typename Type::Scalar>())) {
|
|
return false;
|
|
}
|
|
|
|
if (arr.ndim() != Type::NumIndices) {
|
|
return false;
|
|
}
|
|
|
|
constexpr bool is_aligned = (Options & Eigen::Aligned) != 0;
|
|
|
|
if (PYBIND11_SILENCE_MSVC_C4127(is_aligned) && !is_tensor_aligned(arr.data())) {
|
|
return false;
|
|
}
|
|
|
|
auto shape = get_shape_for_array<typename Type::Index, Type::NumIndices>(arr);
|
|
|
|
if (!Helper::is_correct_shape(shape)) {
|
|
return false;
|
|
}
|
|
|
|
if (PYBIND11_SILENCE_MSVC_C4127(needs_writeable) && !arr.writeable()) {
|
|
return false;
|
|
}
|
|
|
|
auto result = get_array_data_for_type<typename get_storage_pointer_type<MapType>::SPT,
|
|
needs_writeable>(arr);
|
|
|
|
value.reset(new MapType(std::move(result), std::move(shape)));
|
|
|
|
return true;
|
|
}
|
|
|
|
static handle cast(MapType &&src, return_value_policy policy, handle parent) {
|
|
return cast_impl(&src, policy, parent);
|
|
}
|
|
|
|
static handle cast(const MapType &&src, return_value_policy policy, handle parent) {
|
|
return cast_impl(&src, policy, parent);
|
|
}
|
|
|
|
static handle cast(MapType &src, return_value_policy policy, handle parent) {
|
|
if (policy == return_value_policy::automatic
|
|
|| policy == return_value_policy::automatic_reference) {
|
|
policy = return_value_policy::copy;
|
|
}
|
|
return cast_impl(&src, policy, parent);
|
|
}
|
|
|
|
static handle cast(const MapType &src, return_value_policy policy, handle parent) {
|
|
if (policy == return_value_policy::automatic
|
|
|| policy == return_value_policy::automatic_reference) {
|
|
policy = return_value_policy::copy;
|
|
}
|
|
return cast(&src, policy, parent);
|
|
}
|
|
|
|
static handle cast(MapType *src, return_value_policy policy, handle parent) {
|
|
if (policy == return_value_policy::automatic) {
|
|
policy = return_value_policy::take_ownership;
|
|
} else if (policy == return_value_policy::automatic_reference) {
|
|
policy = return_value_policy::reference;
|
|
}
|
|
return cast_impl(src, policy, parent);
|
|
}
|
|
|
|
static handle cast(const MapType *src, return_value_policy policy, handle parent) {
|
|
if (policy == return_value_policy::automatic) {
|
|
policy = return_value_policy::take_ownership;
|
|
} else if (policy == return_value_policy::automatic_reference) {
|
|
policy = return_value_policy::reference;
|
|
}
|
|
return cast_impl(src, policy, parent);
|
|
}
|
|
|
|
template <typename C>
|
|
static handle cast_impl(C *src, return_value_policy policy, handle parent) {
|
|
object parent_object;
|
|
constexpr bool writeable = !std::is_const<C>::value;
|
|
switch (policy) {
|
|
case return_value_policy::reference:
|
|
parent_object = none();
|
|
break;
|
|
|
|
case return_value_policy::reference_internal:
|
|
// Default should do the right thing
|
|
if (!parent) {
|
|
pybind11_fail("Cannot use reference internal when there is no parent");
|
|
}
|
|
parent_object = reinterpret_borrow<object>(parent);
|
|
break;
|
|
|
|
case return_value_policy::take_ownership:
|
|
delete src;
|
|
// fallthrough
|
|
default:
|
|
// move, take_ownership don't make any sense for a ref/map:
|
|
pybind11_fail("Invalid return_value_policy for Eigen Map type, must be either "
|
|
"reference or reference_internal");
|
|
}
|
|
|
|
auto result = array_t<typename Type::Scalar, compute_array_flag_from_tensor<Type>()>(
|
|
convert_dsizes_to_vector(Helper::get_shape(*src)),
|
|
src->data(),
|
|
std::move(parent_object));
|
|
|
|
if (!writeable) {
|
|
array_proxy(result.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
|
|
}
|
|
|
|
return result.release();
|
|
}
|
|
|
|
#if EIGEN_VERSION_AT_LEAST(3, 4, 0)
|
|
|
|
static constexpr bool needs_writeable = !std::is_const<typename std::remove_pointer<
|
|
typename get_storage_pointer_type<MapType>::SPT>::type>::value;
|
|
#else
|
|
// Handle Eigen bug
|
|
static constexpr bool needs_writeable = !std::is_const<Type>::value;
|
|
#endif
|
|
|
|
protected:
|
|
// TODO: Move to std::optional once std::optional has more support
|
|
std::unique_ptr<MapType> value;
|
|
|
|
public:
|
|
static constexpr auto name = get_tensor_descriptor<Type, true, needs_writeable>::value;
|
|
explicit operator MapType *() { return value.get(); }
|
|
explicit operator MapType &() { return *value; }
|
|
explicit operator MapType &&() && { return std::move(*value); }
|
|
|
|
template <typename T_>
|
|
using cast_op_type = ::pybind11::detail::movable_cast_op_type<T_>;
|
|
};
|
|
|
|
PYBIND11_NAMESPACE_END(detail)
|
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|