I am writing tests on Eigen matrices using Google's testing framework Google-Mock, as already discussed in another question.
With the following code I was able to add a custom Matcher
to match Eigen matrices to a given precision.
MATCHER_P2(EigenApproxEqual, expect, prec,
std::string(negation ? "isn't" : "is") + " approx equal to" +
::testing::PrintToString(expect) + "\nwith precision " +
::testing::PrintToString(prec)) {
return arg.isApprox(expect, prec);
}
What this does is to compare two Eigen matrices by their isApprox
method, and if they don't match Google-Mock will print a corresponding error message, which will contain the expected, and the actual values of the matrices. Or, it should at least...
Take the following simple test case:
TEST(EigenPrint, Simple) {
Eigen::Matrix2d A, B;
A << 0., 1., 2., 3.;
B << 0., 2., 1., 3.;
EXPECT_THAT(A, EigenApproxEqual(B, 1e-7));
}
This test will fail because A
, and B
are not equal. Unfortunately, the corresponding error message looks like this:
gtest_eigen_print.cpp:31: Failure
Value of: A
Expected: is approx equal to32-byte object <00-00 00-00 00-00 00-00 00-00 00-00 00-00 F0-3F 00-00 00-00 00-00 00-40 00-00 00-00 00-00 08-40>
with precision 1e-07
Actual: 32-byte object <00-00 00-00 00-00 00-00 00-00 00-00 00-00 00-40 00-00 00-00 00-00 F0-3F 00-00 00-00 00-00 08-40>
As you can see, Google-Test prints a hex-dump of the matrices, instead of a nicer representation of their values. The Google-documentation says the following about printing values of custom types:
This printer knows how to print built-in C++ types, native arrays, STL containers, and any type that supports the << operator. For other types, it prints the raw bytes in the value and hopes that you the user can figure it out.
The Eigen matrix comes with an operator<<
. However, Google-Test, or the C++ compiler, rather, ignores it. To my understanding, for the following reason: The signature of this operator reads (IO.h (line 240))
template<typename Derived>
std::ostream &operator<< (std::ostream &s, const DenseBase<Derived> &m);
I.e. it takes a const DenseBase<Derived>&
. The Google-test hex-dump default printer on the other hand is the default implementation of a template function. You can find the implementation here. (Follow the call-tree starting from PrintTo to see that this is the case, or prove me wrong. ;))
So, the Google-Test default printer is a better match because it takes a const Derived &
, and not only its base class const DenseBase<Derived> &
.
My question is the following. How can I tell the compiler to prefer the Eigen specific operator <<
over the Google-test hex-dump? Under the assumption that I cannot modify the class definition of the Eigen matrix.
So far, I've tried the following things.
Defining a function
template <class Derived>
void PrintTo(const Eigen::DensBase<Derived> &m, std::ostream *o);
won't work for the same reason for which operator<<
doesn't work.
The only thing which I found that worked is to use Eigen's plugin mechanism.
With a file eigen_matrix_addons.hpp
:
friend void PrintTo(const Derived &m, ::std::ostream *o) {
*o << "\n" << m;
}
and the following include directive
#define EIGEN_MATRIXBASE_PLUGIN "eigen_matrix_addons.hpp"
#include <Eigen/Dense>
the test will produce the following output:
gtest_eigen_print.cpp:31: Failure
Value of: A
Expected: is approx equal to
0 2
1 3
with precision 1e-07
Actual:
0 1
2 3
For Eigen matrices this is probably an acceptable solution. However, I know that I will have to apply the same thing to other template classes, very soon, which unfortunately, do not offer a plugin mechanism like Eigen's, and whose definitions I don't have direct access to.
Hence, my question is: Is there a way to point the compiler to the right operator<<
, or PrintTo
function, without modifying the class' definition itself?
#include <Eigen/Dense>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <gmock/gmock-matchers.h>
// A GMock matcher for Eigen matrices.
MATCHER_P2(EigenApproxEqual, expect, prec,
std::string(negation ? "isn't" : "is") + " approx equal to" +
::testing::PrintToString(expect) + "\nwith precision " +
::testing::PrintToString(prec)) {
return arg.isApprox(expect, prec);
}
TEST(EigenPrint, Simple) {
Eigen::Matrix2d A, B;
A << 0., 1., 2., 3.;
B << 0., 2., 1., 3.;
EXPECT_THAT(A, EigenApproxEqual(B, 1e-7));
}
I made some progress with an SFINAE approach.
First, I defined a trait for Eigen types. With it we can use std::enable_if
to provide template functions only for types that fulfill this trait.
#include <type_traits>
#include <Eigen/Dense>
template <class Derived>
struct is_eigen : public std::is_base_of<Eigen::DenseBase<Derived>, Derived> {
};
My first thought was to provide such a version of PrintTo
. Unfortunately, the compiler complains about an ambiguity between this function, and the Google-Test internal default. Is there a way to disambiguate and point the compiler to my function?
namespace Eigen {
// This function will cause the following compiler error, when defined inside
// the Eigen namespace.
// gmock-1.7.0/gtest/include/gtest/gtest-printers.h:600:5: error:
// call to 'PrintTo' is ambiguous
// PrintTo(value, os);
// ^~~~~~~
//
// It will simply be ignore when defined in the global namespace.
template <class Derived,
class = typename std::enable_if<is_eigen<Derived>::value>::type>
void PrintTo(const Derived &m, ::std::ostream *o) {
*o << "\n" << m;
}
}
Another approach is to overload the operator<<
for the Eigen type. It does actually work. However, the downside is that it is a global overload of the ostream operator. So, it is impossible to define any test-specific formatting (e.g. the additional new-line) without this change also affecting non-testing code. Hence, I would prefer a specialized PrintTo
like the one above.
template <class Derived,
class = typename std::enable_if<is_eigen<Derived>::value>::type>
::std::ostream &operator<<(::std::ostream &o, const Derived &m) {
o << "\n" << static_cast<const Eigen::DenseBase<Derived> &>(m);
return o;
}
In the following code I implement the solution by @Alex and implement a small function that converts references of Eigen matrices to the printable type.
#include <Eigen/Dense>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <gmock/gmock-matchers.h>
MATCHER_P(EigenEqual, expect,
std::string(negation ? "isn't" : "is") + " equal to" +
::testing::PrintToString(expect)) {
return arg == expect;
}
template <class Base>
class EigenPrintWrap : public Base {
friend void PrintTo(const EigenPrintWrap &m, ::std::ostream *o) {
*o << "\n" << m;
}
};
template <class Base>
const EigenPrintWrap<Base> &print_wrap(const Base &base) {
return static_cast<const EigenPrintWrap<Base> &>(base);
}
TEST(Eigen, Matrix) {
Eigen::Matrix2i A, B;
A << 1, 2,
3, 4;
B = A.transpose();
EXPECT_THAT(print_wrap(A), EigenEqual(print_wrap(B)));
}
The problems you encounter are overload resolution problems.
google test implements a template function
namespace testing { namespace internal {
template <typename T>
void PrintTo(const T& value, std::ostream *o) { /* do smth */ }
} }
The Eigen library defines a printer function that is based on derivation. Hence
struct EigenBase { };
std::ostream& operator<< (std::ostream& stream, const EigenBase& m) { /* do smth */ }
struct Eigen : public EigenBase { };
void f1() {
Eigen e;
std::cout << e; // works
}
void f2() {
Eigen e;
print_to(eigen, &std::cout); // works
}
Both do have questionable design.
Google Test should not provide an implementation of PrintTo
but should instead check at compile time whether the user provides a PrintTo
and otherwise call a different default printing function PrintToDefault
. The PrintTo
provides is a better match than the one you provided (according to overload resolution).
on the other hand Eigen's operator<<
is based on derivation and a template function will also be preferred by overload resolution.
Eigen could provide a CRTP base class that inherits the operator<<
which a better matching type.
What you can do is inherit from eigen and provide a CRTP overload to your inherited class avoiding the issue.
#include <gtest/gtest.h>
#include <iostream>
class EigenBase {
};
std::ostream &operator<<(std::ostream &o, const EigenBase &r) {
o << "operator<< EigenBase called";
return o;
}
template <typename T>
void print_to(const T &t, std::ostream *o) {
*o << "Google Print To Called";
}
class EigenSub : public EigenBase {};
template <typename T>
struct StreamBase {
typedef T value_type;
// friend function is inline and static
friend std::ostream &operator<<(std::ostream &o, const value_type &r) {
o << "operator<< from CRTP called";
return o;
}
friend void print_to(const value_type &t, std::ostream *o) {
*o << "print_to from CRTP called";
}
};
// this is were the magic appears, because the oeprators are actually
// defined with signatures matching the MyEigenSub class.
class MyEigenSub : public EigenSub, public StreamBase<MyEigenSub> {
};
TEST(EigenBasePrint, t1) {
EigenBase e;
std::cout << e << std::endl; // works
}
TEST(EigenBasePrint, t2) {
EigenBase e;
print_to(e, &std::cout); // works
}
TEST(EigenSubPrint, t3) {
EigenSub e;
std::cout << e << std::endl; // works
}
TEST(EigenCRTPPrint, t4) {
MyEigenSub e;
std::cout << e << std::endl; // operator<< from CRTP called
}
TEST(EigenCRTPPrint, t5) {
MyEigenSub e;
print_to(e, &std::cout); // prints print_to from CRTP called
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With