I am writing tests on Eigen matrices using Google\'s testing framework Google-Mock, as already discussed in another question.
With the followi
I feel compelled to provide a new answer that I believe is simpler and better than the others, although it is so simple that I may have missed something. It's very similar to solutions that you have already tried but it's not quite the same.
Essentially, you don't have to jump through the plugin hoops of modifying the class. The caveat is that, yes, you have to define a PrintTo
function for each type (Matrix2d
, Matrix3d
, etc); a function template won't work. But since this is a unit test, I assume that you know what all your types are and so that's not an issue.
So essentially take your code from the plugin and just put it in the unit test like you were trying to do with the templated SFINAE-enabled one:
namespace Eigen
{
void PrintTo(const Matrix2d &m, std::ostream *os)
{
*os << std::endl << m << std::endl;
}
}
Nothing fancy. This works for me and should do what you want according to your test case and question.
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
}
Considering the OPs answer I want to do some clarifications. Unlike the derived solution from the OP I actually wanted to decorate the class instead of using a function wrapper inside the assertion.
For the sake of simplicity instead of using a google test match predicate I overloaded operator==
.
Instead of using the Eigen class itself we use a wrapper that is a complete replacement of Eigen. So whenever we would create an instance of Eigen
we create an instance of WrapEigen
instead.
Because we do not intent to alter the implementation of Eigen
derivation is fine.
Furthermore we want to add functions to the wrapper. I do this here with multiple inheritance of functor like classes named StreamBase
and EqualBase
. We use CRTP in these functors to get the signatures right.
In order to save potential typing I used a variadic template constructor in Wrapper
. It calls the corresponding base constructor if one exists.
#include <gtest/gtest.h>
#include <iostream>
#include <utility>
using namespace testing::internal;
struct EigenBase {
explicit EigenBase(int i) : priv_(i) {}
friend std::ostream &operator<<(std::ostream &o, const EigenBase &r) {
o << r.priv_;
return o;
}
friend bool operator==(const EigenBase& a, const EigenBase& b) {
return a.priv_ == b.priv_;
}
int priv_;
};
struct Eigen : public EigenBase {
explicit Eigen(int i) : EigenBase(i) {}
};
template <typename T, typename U>
struct StreamBase {
typedef T value_type;
typedef const value_type &const_reference;
friend void PrintTo(const value_type &t, std::ostream *o) {
*o << static_cast<const U&>(t);
}
};
template <typename T, typename U>
struct EqualBase {
typedef T value_type;
typedef const T &const_reference;
friend bool operator==(const_reference a, const_reference b) {
return static_cast<const U&>(a)
== static_cast<const U&>(b);
}
};
template <typename T, typename U>
struct Wrapper
: public T,
public StreamBase<Wrapper<T,U>, U>,
public EqualBase<Wrapper<T,U>, U> {
template <typename... Args>
Wrapper(Args&&... args) : T(std::forward<Args>(args)...) { }
};
TEST(EigenPrint, t1) {
Eigen e(10);
Eigen f(11);
ASSERT_EQ(e,f); // calls gtest::PrintTo
}
TEST(WrapEigenPrint, t1) {
typedef Wrapper<Eigen, EigenBase> WrapEigen;
WrapEigen e(10);
WrapEigen f(11);
ASSERT_EQ(e,f); // calls our own.
}