forked from RobotLocomotion/drake
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathis_memcpy_movable.h
47 lines (41 loc) · 1.83 KB
/
is_memcpy_movable.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#pragma once
#include <cstdlib>
#include <cstring>
#include <functional>
#include <memory>
#include <type_traits>
namespace drake {
namespace test {
/// Checks if @p value of @p T type is movable via memcpy. That is, it tests
/// memcpy on @p value keeps a given invariant between @p value and a copy of
/// it. It uses a binary function object @p invariant_pred to check for
/// invariance.
///
/// @note Eigen's reallocation mechanisms have an issue. It is moving bytes
/// using `memcpy` without calling a move constructor. As a result, if a
/// `Scalar` type of Eigen matrix/array is not IsMemcpyMovable, we have
/// undefined behavior when `conservativeResize` method is called. This should
/// *always* be used to test a `Scalar` used within an `Eigen::EigenBase<...>`
/// object. Please see https://github.com/RobotLocomotion/drake/issues/5974 for
/// more information.
template <typename T, typename InvariantPred = std::equal_to<T>>
[[nodiscard]] bool IsMemcpyMovable(
const T& value, const InvariantPred& invariant_pred = InvariantPred()) {
// 1. Create ptr_to_original via placement-new.
auto original_storage = std::make_unique<
typename std::aligned_storage<sizeof(T), alignof(T)>::type>();
T* const ptr_to_original{new (original_storage.get()) T{value}};
// 2. Create ptr_to_moved from ptr_to_original via memcpy.
auto moved_storage = std::make_unique<
typename std::aligned_storage<sizeof(T), alignof(T)>::type>();
T* const ptr_to_moved{reinterpret_cast<T* const>(moved_storage.get())};
memcpy(static_cast<void*>(ptr_to_moved), ptr_to_original, sizeof(T));
// 3. Free original_storage.
original_storage.reset();
// 4. Check if the invariant between value and ptr_to_moved hold.
const bool out{invariant_pred(value, *ptr_to_moved)};
ptr_to_moved->T::~T();
return out;
}
} // namespace test
} // namespace drake