教Google - 测试如何打印特征矩阵 [英] Teach Google-Test how to print Eigen Matrix
问题描述
简介
我使用Google的测试框架Google-Mock对Eigen矩阵写测试,如另一个问题。
使用下面的代码,我可以添加一个自定义 Matcher
来匹配Eigen矩阵到给定的精度。
MATCHER_P2(EigenApproxEqual,expect,prec,
std :: string(negation?is not:is)+approx equal to+
::测试:: PrintToString(expect)+\\\
with precision+
:: testing :: PrintToString(prec)){
return arg.isApprox(expect,prec);
}
这是做什么的是比较两个特征矩阵 isApprox
方法,如果它们不匹配Google-Mock将打印相应的错误消息,其中将包含矩阵的预期值和实际值。或者,至少应该...
问题
采取以下简单测试用例: p>
TEST(EigenPrint,Simple){
Eigen :: Matrix2d A,B;
A<< 0.,1.,2.,3。
B<< 0..2,1.,3。
EXPECT_THAT(A,EigenApproxEqual(B,1e-7));
}
此测试将失败,因为 A
和 B
不相等。不幸的是,相应的错误信息是这样的:
gtest_eigen_print.cpp:A $:31:失败
值b $ b预计:大约是相等至32字节的对象< 00-00 00-00 00-00 00-00 00-00 00-00 00-00 00-00 F0-3F 00-00 00-00 00-40 00-00 00-00 00-00 08-40>
精密1E-07
实际:32字节的对象< 00-00 00-00 00-00 00-00 00-00 00-00 00-00 00-40 00-00 00-99 00 00-00 F0-3F 00-00 00-00 00-00 08-40>
如您所见,Google-Test打印了矩阵的十六进制转储,而不是更好表示他们的价值观。该谷歌的文档说以下有关自定义类型的打印值:
这打印机知道如何打印内置的C ++类,原生数组,STL
的容器,和任何支持类型的<< ;运算符。对于其他
类型,它打印在价值的原始字节,希望您在
用户自己看着办吧。
块引用>
本征矩阵配备了一个
运营商的LT;<
。但是,Google-Test或C ++编译器忽略它。据我了解,原因如下:此运算符的签名读取( IO.h(线240))模板< typename Derived>
std :: ostream& operator<< (std :: ostream& s,const DenseBase< Derived>& m);
它需要一个
const DenseBase< Derived>&
。另一方面,Google-test hex-dump默认打印机是模板函数的默认实现。您可以在此处。 (按照调用树从开始Printto 看到这种情况,或证明我错了。))
因此,Google-Test默认打印机是一个更好的匹配,因为它取得
const Derived&
,而不仅是其基类const DenseBase< Derived> &
。
我的问题
我的问题是以下。我如何告诉编译器优先于Google-test hex-dump的特征
operator<
?假设我不能修改Eigen矩阵的类定义。
我的尝试
到目前为止,我已经尝试过以下操作。
定义函数
template< class Derived>
void PrintTo(const Eigen :: DensBase< Derived>& m,std :: ostream * o);
将不能用于
运算符<< / code>不起作用。
我发现唯一的工作是使用Eigen的插件机制。
使用文件
eigen_matrix_addons.hpp
:friend PrintTo(const Derived& m,:: std :: ostream * o){
* o<< \\\
< m;
}
和以下include伪指令
#define EIGEN_MATRIXBASE_PLUGINeigen_matrix_addons.hpp
#include< Eigen / Dense>
测试将产生以下输出:
gtest_eigen_print.cpp:31:失败
值:A
预期:约等于
0 2
1 3
精确1e-07
实际:
0 1
2 3
$ b b这有什么问题?
对于Eigen矩阵,这可能是一个可以接受的解决方案。但是,我知道,我将不得不应用相同的东西到其他模板类,很快,不幸的是,不提供像Eigen的插件机制,其定义我不能直接访问。
因此,我的问题是:是否有一种方法指向编译器正确
运算符<
或PrintTo
函数,而不修改类的定义本身?
/ h1>
#include< Eigen / Dense>
#include< gtest / gtest.h>
#include< gmock / gmock.h>
#include< gmock / gmock-matchers.h>
//用于Eigen矩阵的GMock匹配器。
MATCHER_P2(EigenApproxEqual,expect,prec,
std :: string(negation?is not: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));
}
编辑:进一步尝试
我使用SFINAE方法取得了一些进展。
首先,我为Eigen类型定义了一个trait。使用它,我们可以使用
std :: enable_if
为满足这个特征的类型提供模板函数。#include< type_traits>
#include< Eigen / Dense>
template< class Derived>
struct is_eigen:public std :: is_base_of< Eigen :: DenseBase< Derived> Derived> {
};
我的第一个想法是提供
PrintTo
。不幸的是,编译器抱怨这个函数和Google-Test内部默认值之间的模糊性。 有没有办法消除歧义并将编译器指向我的函数?命名空间Eigen {
//当在
//内部定义Eigen命名空间时,此函数将导致以下编译器错误。
// gmock-1.7.0 / gtest / include / gtest / gtest-printers.h:600:5:错误:
//调用'PrintTo'是不明确的
// PrintTo (value,os);
// ^ ~~~~~~
//
//在全局命名空间中定义时,它将被忽略。
template< class Derived,
class = typename std :: enable_if< is_eigen< Derived> :: value> :: type>
void PrintTo(const Derived& m,:: std :: ostream * o){
* o& \\\
< m;
}
}
另一种方法是重载
operator<<
。它的确实际工作。然而,缺点是它是ostream运算符的全局过载。因此,不可能定义任何特定于测试的格式化(例如,额外的新行),而这种更改也会影响非测试代码。因此,我喜欢像上面一样的专门PrintTo
。template< class Derived,
class = typename std :: enable_if< is_eigen< Derived> :: value> :: type>
:: std :: ostream& operator<<<(:: std :: ostream& o,const Derived& m){
o& \\\
< static_cast< const Eigen :: DenseBase< Derived> &>(m);
return o;
}
编辑:回答
在下面的代码中,我通过@Alex实现该解决方案,并实现一个小函数,将Eigen矩阵的引用转换为可打印类型。
#include< Eigen / Dense>
#include> gtest / gtest.h>
#include< gmock / gmock.h>
#include< gmock / gmock-matchers.h>
MATCHER_P(EigenEqual,expect,
std :: string(negation?is not: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& \\\
< m;
}
};
template< class Base>
const EigenPrintWrap< Base> & print_wrap(const Base& base){
return static_cast< const EigenPrintWrap< 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)));
}
解决方案解决问题。
google测试实现了一个模板函数
命名空间测试{namespace internal {
pre>
template< typename T>
void PrintTo(const T& value,std :: ostream * o){/ * do smth * /}
}}
Eigen库定义了一个基于派生的打印机函数。因此
struct EigenBase {};
std :: ostream&运算符<< (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
}
两者都有可疑的设计。
Google Test不应提供
PrintTo
的实现,而应该在编译时检查用户是否提供PrintTo
,否则调用不同的默认打印函数PrintToDefault
。PrintTo
提供的是比您提供的更好的匹配(根据重载解析)。
手Eigen的
运算符<<
是基于推导的,模板函数也将被重载决议优先考虑。
Eigen可以提供一个继承了
运算符<<
的CRTP基类,这是更好的匹配类型。
可以做是从eigen继承,并为您的继承类提供CRTP重载,避免此问题。
#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函数是inline和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;
}
};
//这是神奇的出现,因为oeprators实际上是
//使用与MyEigenSub类匹配的签名来定义。
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<<从CRTP调用
}
TEST(EigenCRTPPrint,t5){
MyEigenSub e;
print_to(e,& std :: cout); // print print from CRTP called
}
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
, andB
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 classconst 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<<
, orPrintTo
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 specializedPrintTo
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))); }
解决方案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 aPrintTo
and otherwise call a different default printing functionPrintToDefault
. ThePrintTo
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 }
这篇关于教Google - 测试如何打印特征矩阵的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!