Fixed some typos and reformulated a few sentences.

This commit is contained in:
Hauke Heibel 2010-08-04 16:40:33 +02:00
parent 224dd66e10
commit d558e84f0b

View File

@ -2,9 +2,9 @@ namespace Eigen {
/** \page TopicFunctionTakingEigenTypes Writing Functions Taking Eigen Types as Parameters
Eigen is making heavy use of expression templates and as a consequence almost all operations on Matrices and Arrays result in individual objects. The effect of this design is that without writing template functions it is not possible to provide \e generically applicable methods taking simple references to Matrices or Arrays.
Eigen's use of expression templates results in almost all operations being objects of a different type. The effect of this design is that without writing template functions it is not possible to provide \e generically applicable methods taking simple references to Matrices or Arrays.
This page explains how to write generic functions taking Eigen types and potential pitfalls one should be aware of in order to write safe code.
This page explains how to write generic functions taking Eigen types and potential pitfalls one should be aware of in order to write safe and efficient code.
<b>Table of contents</b>
- \ref TopicSimpleFunctionsWorking
@ -24,7 +24,7 @@ MatrixXf cov(const MatrixXf& x, const MatrixXf& y)
return (x.rowwise() - x_mean).transpose() * (y.rowwise() - y_mean) / num_observations;
}
\endcode
and contrary to what one think at first, this implementation is fine unless you require a genric implementation that works with double matrices too and unless you do not care about temporary objects. Why is that the case? Where are you seeing temporaries? How can code like this compile?
and contrary to what one might think at first, this implementation is fine unless you require a genric implementation that works with double matrices too and unless you do not care about temporary objects. Why is that the case? Where are temporaries involved? How can code as given below compile?
\code
MatrixXf x,y,z;
MatrixXf C = cov(x,y+z);
@ -35,7 +35,7 @@ In this special case, the example is fine and will be working because both param
\section TopicSimpleFunctionsFailing In which cases do simple functions fail?
Here, we consider a slightly modified version of the function given above. This time, we do not want to return the result but pass an additional non-const paramter which allows us to store the result. The function turns out being implemented as follows.
Here, we consider a slightly modified version of the function given above. This time, we do not want to return the result but pass an additional non-const paramter which allows us to store the result. A first naive implementation might look as follows.
\code
// Note: This code is flawed!
void cov(const MatrixXf& x, const MatrixXf& y, MatrixXf& C)
@ -51,7 +51,7 @@ When trying to execute the following code
MatrixXf C = MatrixXf::Zero(3,6);
cov(x,y, C.block(0,0,3,3));
\endcode
the compiler will fail, because it is not possible to convert the expression returned by \c MatrixXf::block in a non-const \c MatrixXf&. This is the case because the compiler wants to protect you from writing your result to a temporary object. In this special case on the other-hand-side this protection is not intended -- we want to write to a temporary object. So how can we overcome this problem? There are two possible solutions depending on the type of compiler you are using.
the compiler will fail, because it is not possible to convert the expression returned by \c MatrixXf::block() in a non-const \c MatrixXf&. This is the case because the compiler wants to protect you from writing your result to a temporary object. In this special case this protection is not intended -- we want to write to a temporary object. So how can we overcome this problem? There are two possible solutions depending on the type of compiler you are using.
<b>Solution A)</b>
Assuming you are using a compiler following the C98 standard, the only thing you can do is to use a little \em hack. You need to pass a const reference and internally the constness needs to be cast away. The correct implementation for C98 compliant compilers would be
@ -71,7 +71,7 @@ void cov(const MatrixBase<Derived>& x, const MatrixBase<Derived>& y, MatrixBase<
(x.rowwise() - x_mean).transpose() * (y.rowwise() - y_mean) / num_observations;
}
\endcode
The implementation above does now not only work with temporary expressions but it also allows to use the function with Matrices of all floating point scalar types.
The implementation above does now not only work with temporary expressions but it also allows to use the function with matrices of arbitrary floating point scalar types.
\b Note: The const cast hack will only work with templated functions. It will not work with the MatrixXf implementation because it is not possible to cast a Block expression to a Matrix reference!
@ -93,6 +93,8 @@ void cov(const MatrixBase<Derived>& x, const MatrixBase<Derived>& y, MatrixBase<
}
\endcode
\b Note: Currently, this fails to work on Visual Studio 2010 even though the compiler is assumed to be supporting rvalue references. So stick to the EIGEN_REF_TO_TEMPORARY \em hack.
\section TopicResizingInGenericImplementations How to resize matrices in generic implementations?
One might think we are done now, right? This is not completely true because in order for our covariance function to be generically applicable, we want the follwing code to work
@ -127,24 +129,10 @@ This implementation is now working for paramters being expressions and for param
\section TopicSummary Summary
To summarize, the implementation of functions taking non-writable (const referenced) objects is not a big issue and does not lead to problematic situations. Writing functions taking parameters that are writable (non-const) requires to implement the functions such that they take references to MatrixBase (or ArrayBase). Furthermore, writable objects require to use Eigen's macro EIGEN_REF_TO_TEMPORARY and casting away constness within the function.
- To summarize, the implementation of functions taking non-writable (const referenced) objects is not a big issue and does not lead to problematic situations in terms of compiling and running your program. However, such an implementation is likely to introduce unnecessary temporary objects in your code. In case you care about optimal run-time performance, use (const) references to MatrixBase or ArrayBase, i.e. write templated functions.
Regarding resizing objects, in particular in functions that take as parameters MatrixBase (or ArrayBase), the actual resizing has to be performed on the derived class.
- Writing functions taking parameters that are writable (non-const) requires to implement the functions such that they take references to MatrixBase or ArrayBase. Furthermore, writable objects require to use Eigen's macro EIGEN_REF_TO_TEMPORARY and casting away constness within the function.
Finally a short note on rvalue references. In Visual Studio 10, rvalue references do not work as expected. Since the follwing code does not compile
\code
struct Base {};
struct Derived : public Base {};
void buggy(Base&& b) {}
int main()
{
Derived d;
buggy(d);
}
\endcode
it is currently not possible to use rvalue references in Visual Studio 2010 in the scenario as described above because the covariance function would reject to accept MatrixXf as a parameter.
- Regarding resizing objects, in particular in functions that take as parameters MatrixBase (or ArrayBase), the actual resizing has to be performed on the derived class.
*/
}