Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 1 | namespace Eigen { |
| 2 | |
| 3 | /** \page TopicPitfalls Common pitfalls |
| 4 | |
| 5 | |
| 6 | \section TopicPitfalls_template_keyword Compilation error with template methods |
| 7 | |
| 8 | See this \link TopicTemplateKeyword page \endlink. |
| 9 | |
Austin Schuh | c55b017 | 2022-02-20 17:52:35 -0800 | [diff] [blame] | 10 | |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 11 | \section TopicPitfalls_aliasing Aliasing |
| 12 | |
| 13 | Don't miss this \link TopicAliasing page \endlink on aliasing, |
| 14 | especially if you got wrong results in statements where the destination appears on the right hand side of the expression. |
| 15 | |
Austin Schuh | c55b017 | 2022-02-20 17:52:35 -0800 | [diff] [blame] | 16 | |
| 17 | \section TopicPitfalls_alignment_issue Alignment Issues (runtime assertion) |
| 18 | |
| 19 | %Eigen does explicit vectorization, and while that is appreciated by many users, that also leads to some issues in special situations where data alignment is compromised. |
| 20 | Indeed, prior to C++17, C++ does not have quite good enough support for explicit data alignment. |
| 21 | In that case your program hits an assertion failure (that is, a "controlled crash") with a message that tells you to consult this page: |
| 22 | \code |
| 23 | http://eigen.tuxfamily.org/dox/group__TopicUnalignedArrayAssert.html |
| 24 | \endcode |
| 25 | Have a look at \link TopicUnalignedArrayAssert it \endlink and see for yourself if that's something that you can cope with. |
| 26 | It contains detailed information about how to deal with each known cause for that issue. |
| 27 | |
| 28 | Now what if you don't care about vectorization and so don't want to be annoyed with these alignment issues? Then read \link getrid how to get rid of them \endlink. |
| 29 | |
| 30 | |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 31 | \section TopicPitfalls_auto_keyword C++11 and the auto keyword |
| 32 | |
Austin Schuh | c55b017 | 2022-02-20 17:52:35 -0800 | [diff] [blame] | 33 | In short: do not use the auto keywords with %Eigen's expressions, unless you are 100% sure about what you are doing. In particular, do not use the auto keyword as a replacement for a \c Matrix<> type. Here is an example: |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 34 | |
| 35 | \code |
| 36 | MatrixXd A, B; |
| 37 | auto C = A*B; |
| 38 | for(...) { ... w = C * v; ...} |
| 39 | \endcode |
| 40 | |
Austin Schuh | c55b017 | 2022-02-20 17:52:35 -0800 | [diff] [blame] | 41 | In this example, the type of C is not a \c MatrixXd but an abstract expression representing a matrix product and storing references to \c A and \c B. |
| 42 | Therefore, the product of \c A*B will be carried out multiple times, once per iteration of the for loop. |
| 43 | Moreover, if the coefficients of `A` or `B` change during the iteration, then `C` will evaluate to different values as in the following example: |
| 44 | |
| 45 | \code |
| 46 | MatrixXd A = ..., B = ...; |
| 47 | auto C = A*B; |
| 48 | MatrixXd R1 = C; |
| 49 | A = ...; |
| 50 | MatrixXd R2 = C; |
| 51 | \endcode |
| 52 | for which we end up with `R1` ≠ `R2`. |
| 53 | |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 54 | |
| 55 | Here is another example leading to a segfault: |
| 56 | \code |
| 57 | auto C = ((A+B).eval()).transpose(); |
| 58 | // do something with C |
| 59 | \endcode |
Austin Schuh | c55b017 | 2022-02-20 17:52:35 -0800 | [diff] [blame] | 60 | The problem is that \c eval() returns a temporary object (in this case a \c MatrixXd) which is then referenced by the \c Transpose<> expression. |
| 61 | However, this temporary is deleted right after the first line, and then the \c C expression references a dead object. |
| 62 | One possible fix consists in applying \c eval() on the whole expression: |
| 63 | \code |
| 64 | auto C = (A+B).transpose().eval(); |
| 65 | \endcode |
| 66 | |
| 67 | The same issue might occur when sub expressions are automatically evaluated by %Eigen as in the following example: |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 68 | \code |
| 69 | VectorXd u, v; |
| 70 | auto C = u + (A*v).normalized(); |
| 71 | // do something with C |
| 72 | \endcode |
Austin Schuh | c55b017 | 2022-02-20 17:52:35 -0800 | [diff] [blame] | 73 | Here the \c normalized() method has to evaluate the expensive product \c A*v to avoid evaluating it twice. |
| 74 | Again, one possible fix is to call \c .eval() on the whole expression: |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 75 | \code |
| 76 | auto C = (u + (A*v).normalized()).eval(); |
| 77 | \endcode |
Austin Schuh | c55b017 | 2022-02-20 17:52:35 -0800 | [diff] [blame] | 78 | In this case, \c C will be a regular \c VectorXd object. |
| 79 | Note that DenseBase::eval() is smart enough to avoid copies when the underlying expression is already a plain \c Matrix<>. |
| 80 | |
| 81 | |
| 82 | \section TopicPitfalls_header_issues Header Issues (failure to compile) |
| 83 | |
| 84 | With all libraries, one must check the documentation for which header to include. |
| 85 | The same is true with %Eigen, but slightly worse: with %Eigen, a method in a class may require an additional \c \#include over what the class itself requires! |
| 86 | For example, if you want to use the \c cross() method on a vector (it computes a cross-product) then you need to: |
| 87 | \code |
| 88 | #include<Eigen/Geometry> |
| 89 | \endcode |
| 90 | We try to always document this, but do tell us if we forgot an occurrence. |
| 91 | |
| 92 | |
| 93 | \section TopicPitfalls_ternary_operator Ternary operator |
| 94 | |
| 95 | In short: avoid the use of the ternary operator <code>(COND ? THEN : ELSE)</code> with %Eigen's expressions for the \c THEN and \c ELSE statements. |
| 96 | To see why, let's consider the following example: |
| 97 | \code |
| 98 | Vector3f A; |
| 99 | A << 1, 2, 3; |
| 100 | Vector3f B = ((1 < 0) ? (A.reverse()) : A); |
| 101 | \endcode |
| 102 | This example will return <code>B = 3, 2, 1</code>. Do you see why? |
| 103 | The reason is that in c++ the type of the \c ELSE statement is inferred from the type of the \c THEN expression such that both match. |
| 104 | Since \c THEN is a <code>Reverse<Vector3f></code>, the \c ELSE statement A is converted to a <code>Reverse<Vector3f></code>, and the compiler thus generates: |
| 105 | \code |
| 106 | Vector3f B = ((1 < 0) ? (A.reverse()) : Reverse<Vector3f>(A)); |
| 107 | \endcode |
| 108 | In this very particular case, a workaround would be to call A.reverse().eval() for the \c THEN statement, but the safest and fastest is really to avoid this ternary operator with %Eigen's expressions and use a if/else construct. |
| 109 | |
| 110 | |
| 111 | \section TopicPitfalls_pass_by_value Pass-by-value |
| 112 | |
| 113 | If you don't know why passing-by-value is wrong with %Eigen, read this \link TopicPassingByValue page \endlink first. |
| 114 | |
| 115 | While you may be extremely careful and use care to make sure that all of your code that explicitly uses %Eigen types is pass-by-reference you have to watch out for templates which define the argument types at compile time. |
| 116 | |
| 117 | If a template has a function that takes arguments pass-by-value, and the relevant template parameter ends up being an %Eigen type, then you will of course have the same alignment problems that you would in an explicitly defined function passing %Eigen types by reference. |
| 118 | |
| 119 | Using %Eigen types with other third party libraries or even the STL can present the same problem. |
| 120 | <code>boost::bind</code> for example uses pass-by-value to store arguments in the returned functor. |
| 121 | This will of course be a problem. |
| 122 | |
| 123 | There are at least two ways around this: |
| 124 | - If the value you are passing is guaranteed to be around for the life of the functor, you can use boost::ref() to wrap the value as you pass it to boost::bind. Generally this is not a solution for values on the stack as if the functor ever gets passed to a lower or independent scope, the object may be gone by the time it's attempted to be used. |
| 125 | - The other option is to make your functions take a reference counted pointer like boost::shared_ptr as the argument. This avoids needing to worry about managing the lifetime of the object being passed. |
| 126 | |
| 127 | |
| 128 | \section TopicPitfalls_matrix_bool Matrices with boolean coefficients |
| 129 | |
| 130 | The current behaviour of using \c Matrix with boolean coefficients is inconsistent and likely to change in future versions of Eigen, so please use it carefully! |
| 131 | |
| 132 | A simple example for such an inconsistency is |
| 133 | |
| 134 | \code |
| 135 | template<int Size> |
| 136 | void foo() { |
| 137 | Eigen::Matrix<bool, Size, Size> A, B, C; |
| 138 | A.setOnes(); |
| 139 | B.setOnes(); |
| 140 | |
| 141 | C = A * B - A * B; |
| 142 | std::cout << C << "\n"; |
| 143 | } |
| 144 | \endcode |
| 145 | |
| 146 | since calling \c foo<3>() prints the zero matrix while calling \c foo<10>() prints the identity matrix. |
| 147 | |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 148 | */ |
| 149 | } |