Lemming Lemming - 3 years ago 173
C++ Question

Teach Google-Test how to print Eigen Matrix

Introduction



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...

The Problem



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



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.




My Attempts



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


What's wrong with that?



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?




The Full Code



#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));
}





Edit: Further Attempts



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;
}





Edit: Following @Alex's Answer



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)));
}

Answer Source

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
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download