如何将 BOOST_CHECK_CLOSE 用于用户定义的类型
How to use BOOST_CHECK_CLOSE for User defined types
我想检查以下类型的对象是否符合预期值almost/close。
class MyTypeWithDouble
{
public:
MyTypeWithDouble(double);
bool operator == (const MyTypeWithDouble& rhs) const; //Checks for Equality
private:
double m;
};
/////////////////////////////////////////
class MyTypeWithVector
{
public:
MyTypeWithVector(std::vector<double>v);
bool operator == (const MyTypeWithVector& rhs) const; //Checks for Equality
private:
std::vector<double> v;
};
所以单元测试看起来像这样
/// TEST1 ////////////////
MyTypeWithDouble t1(42.100001);
BOOST_CHECK_CLOSE(t1,42.1,0.1);
//////TEST2//////////////////////////////
std::vector<double> v; //no initalizer do not have c++11 :-(
v.push_back(42.1);
MyTypeWithVector t2(v);
std::vector<double> compare;
compare.push_back(42.100001);
MY_OWN_FUNCTION_USING_BOOST(t2,compare,0.1); //There is only BOOST_CHECK_EQUAL_COLLECTION available for collections
- 我不想更改 '== 运算符的 or 的实现
提供吸气剂。
- 添加其他算子函数即可。
谢谢,ToBe
我认为你设计过度了。我会建议一个简单的宏,也许有一个合适的朋友定义。话虽如此,让我们接受挑战吧。
必要的调整
你的类型应该
- default-constructible 用于
check_is_close_t
实施。
- 此外,必须 有一种获取值的方法,并且由于您拒绝创建 getter,剩下的唯一选择是声明一个访问器class 作为好友
我们得到
class MyTypeWithDouble
{
public:
constexpr MyTypeWithDouble(double v = 0) : m(v) {}
MyTypeWithDouble& operator=(MyTypeWithDouble const&) noexcept = default;
constexpr MyTypeWithDouble(MyTypeWithDouble const&) noexcept = default;
private:
friend class unittest_op::access;
double m;
};
通过一些乏味的工作(在 header?),我们可以利用这个 access
漏洞来实现其他一切。 如何? 好吧,我们定义了一个 "getter",好吧,但是 在 class 定义之外 。
我在 access
中定义了一个特征 class 模板(所以它隐含了一个 friend
),你可以专门针对你的 "floating-point-like" 类型:
namespace unittest_op {
template<> class access::impl<MyTypeWithDouble> {
public:
typedef double result_type;
static result_type call(MyTypeWithDouble const& v) { return v.m; }
};
}
仅此而已。好吧,这就是 你 作为 type/test 实施者的全部内容。当然,我们还需要完成这项工作。
基本要素
存在 unittest_op
命名空间的唯一原因是定义 "relaying" 知道如何访问自定义类型中包含的值的运算符。
请注意我们如何
- 不需要向您的 user-defined 类型添加任何内容
- 我们也得到混合操作数(例如
2 * MyTypeWithDouble(7.0) -> MyTypeWithDouble(14.0)
)
- 并且我们还定义了
operator<<
,因此断言宏知道如何打印 MyTypeWithDouble
的值
感谢c++11工作并不复杂:
namespace unittest_op {
class access {
template<typename T, typename Enable = void> class impl;
template<typename T>
class impl<T, typename std::enable_if<std::is_arithmetic<T>::value, void>::type>
{
public: typedef T result_type;
static T & call(T& v) { return v; }
static T const& call(T const& v) { return v; }
};
public:
template<typename T>
static typename impl<T>::result_type do_access(T const& v) { return impl<T>::call(v); }
template<typename T> static constexpr bool can_access(decltype(do_access(std::declval<T>()))*) { return true; }
template<typename T> static constexpr bool can_access(...) { return false; }
};
template<typename T>
typename std::enable_if<access::can_access<T>(nullptr) && not std::is_arithmetic<T>::value, std::ostream&>::type
operator<<(std::ostream& os, T const& v) { return os << "{" << access::do_access(v) << "}"; }
template <typename T, typename Enable=decltype(access::do_access(std::declval<T>())) >
static T operator-(T const& lhs) { return - access::do_access(lhs); }
template <typename T, typename Enable=decltype(access::do_access(std::declval<T>())) >
static T operator+(T const& lhs) { return + access::do_access(lhs); }
#define UNITTEST_OP_BINOP(OP) \
template <typename T1, typename T2> \
static decltype(access::do_access(std::declval<T1>()) OP access::do_access(std::declval<T2>())) \
operator OP(T1 const& lhs, T2 const& rhs) { return access::do_access(lhs) OP access::do_access(rhs); } \
using ::unittest_op::operator OP;
UNITTEST_OP_BINOP(==)
UNITTEST_OP_BINOP(!=)
UNITTEST_OP_BINOP(< )
UNITTEST_OP_BINOP(> )
UNITTEST_OP_BINOP(<=)
UNITTEST_OP_BINOP(>=)
UNITTEST_OP_BINOP(+ )
UNITTEST_OP_BINOP(- )
UNITTEST_OP_BINOP(% )
UNITTEST_OP_BINOP(* )
UNITTEST_OP_BINOP(/ )
// assign-ops only for lvalue types (i.e. identity `access::impl<T>`)
UNITTEST_OP_BINOP(+=)
UNITTEST_OP_BINOP(-=)
UNITTEST_OP_BINOP(%=)
UNITTEST_OP_BINOP(*=)
UNITTEST_OP_BINOP(/=)
#undef UNITTEST_OP_BINOP
}
请注意,这些都是 "open" 模板,我们已采取必要的预防措施以确保这些运算符 仅 适用,前提是 do_access
是定义 和 该类型不是算术类型。
为什么要采取这些预防措施?
嗯。我们要做一个 power-move:我们将把运算符重载注入 boost::test_tools
命名空间,以便 BOOST_CHECK*
宏实现可以找到它们。
如果我们不采取刚才提到的预防措施,我们会因我们不关心的类型的模糊运算符重载而引发很多问题。
夺权
权力获取很简单:我们在 boost::test_tools
命名空间中注入 (using
) 我们的每个操作符模板。
现在我们可以开始了:
BOOST_AUTO_TEST_CASE(my_test)
{
MyTypeWithDouble v(4);
BOOST_CHECK_CLOSE(3.99, v, MyTypeWithDouble(0.1));
}
版画
Running 2 test cases...
main.cpp(117): error in "my_test": difference{0.25%} between 3.99{3.9900000000000002} and v{{4}} exceeds {0.10000000000000001}%
完整节目
#include <utility>
#include <type_traits>
#include <iostream>
namespace unittest_op {
class access {
template<typename T, typename Enable = void> class impl;
template<typename T>
class impl<T, typename std::enable_if<std::is_arithmetic<T>::value, void>::type>
{
public: typedef T result_type;
static T & call(T& v) { return v; }
static T const& call(T const& v) { return v; }
};
public:
template<typename T>
static typename impl<T>::result_type do_access(T const& v) { return impl<T>::call(v); }
template<typename T> static constexpr bool can_access(decltype(do_access(std::declval<T>()))*) { return true; }
template<typename T> static constexpr bool can_access(...) { return false; }
};
template<typename T>
typename std::enable_if<access::can_access<T>(nullptr) && not std::is_arithmetic<T>::value, std::ostream&>::type
operator<<(std::ostream& os, T const& v) { return os << "{" << access::do_access(v) << "}"; }
template <typename T, typename Enable=decltype(access::do_access(std::declval<T>())) >
static T operator-(T const& lhs) { return - access::do_access(lhs); }
template <typename T, typename Enable=decltype(access::do_access(std::declval<T>())) >
static T operator+(T const& lhs) { return + access::do_access(lhs); }
#define UNITTEST_OP_BINOP(OP) \
template <typename T1, typename T2> \
static decltype(access::do_access(std::declval<T1>()) OP access::do_access(std::declval<T2>())) \
operator OP(T1 const& lhs, T2 const& rhs) { return access::do_access(lhs) OP access::do_access(rhs); } \
using ::unittest_op::operator OP;
UNITTEST_OP_BINOP(==)
UNITTEST_OP_BINOP(!=)
UNITTEST_OP_BINOP(< )
UNITTEST_OP_BINOP(> )
UNITTEST_OP_BINOP(<=)
UNITTEST_OP_BINOP(>=)
UNITTEST_OP_BINOP(+ )
UNITTEST_OP_BINOP(- )
UNITTEST_OP_BINOP(% )
UNITTEST_OP_BINOP(* )
UNITTEST_OP_BINOP(/ )
// assign-ops only for lvalue types (i.e. identity `access::impl<T>`)
UNITTEST_OP_BINOP(+=)
UNITTEST_OP_BINOP(-=)
UNITTEST_OP_BINOP(%=)
UNITTEST_OP_BINOP(*=)
UNITTEST_OP_BINOP(/=)
#undef UNITTEST_OP_BINOP
}
namespace boost { namespace test_tools {
using unittest_op::operator ==;
using unittest_op::operator !=;
using unittest_op::operator < ;
using unittest_op::operator > ;
using unittest_op::operator <=;
using unittest_op::operator >=;
using unittest_op::operator + ;
using unittest_op::operator - ;
using unittest_op::operator % ;
using unittest_op::operator * ;
using unittest_op::operator / ;
using unittest_op::operator +=;
using unittest_op::operator -=;
using unittest_op::operator %=;
using unittest_op::operator *=;
using unittest_op::operator /=;
using unittest_op::operator <<;
} }
class MyTypeWithDouble
{
public:
constexpr MyTypeWithDouble(double v = 0) : m(v) {}
MyTypeWithDouble& operator=(MyTypeWithDouble const&) noexcept = default;
constexpr MyTypeWithDouble(MyTypeWithDouble const&) noexcept = default;
private:
friend class unittest_op::access;
double m;
};
namespace unittest_op {
template<> class access::impl<MyTypeWithDouble> {
public:
typedef double result_type;
static result_type call(MyTypeWithDouble const& v) { return v.m; }
};
}
#define BOOST_TEST_MODULE MyTest
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_CASE(my_test)
{
MyTypeWithDouble v(4);
BOOST_CHECK_CLOSE(3.99, v, MyTypeWithDouble(0.1));
}
BOOST_AUTO_TEST_CASE(general_operator_invocations) // just a testbed to see the overloads are found and compile
{
MyTypeWithDouble v(4);
using namespace unittest_op; // we're not using the test_tools here
BOOST_CHECK(4.00000000000000001 == v);
BOOST_CHECK(4.000000000000001 != v);
#define UNITTEST_OP_BINOP(OP) { \
auto x = v OP static_cast<MyTypeWithDouble>(0.01); \
x = static_cast<MyTypeWithDouble>(0.01) OP v; \
x = v OP v; \
(void) x; \
}
UNITTEST_OP_BINOP(==)
UNITTEST_OP_BINOP(!=)
UNITTEST_OP_BINOP(+ )
UNITTEST_OP_BINOP(- )
//UNITTEST_OP_BINOP(% )
UNITTEST_OP_BINOP(* )
UNITTEST_OP_BINOP(/ )
UNITTEST_OP_BINOP(< )
UNITTEST_OP_BINOP(> )
UNITTEST_OP_BINOP(<=)
UNITTEST_OP_BINOP(>=)
-v == -v;
+v == +v;
}
我想检查以下类型的对象是否符合预期值almost/close。
class MyTypeWithDouble
{
public:
MyTypeWithDouble(double);
bool operator == (const MyTypeWithDouble& rhs) const; //Checks for Equality
private:
double m;
};
/////////////////////////////////////////
class MyTypeWithVector
{
public:
MyTypeWithVector(std::vector<double>v);
bool operator == (const MyTypeWithVector& rhs) const; //Checks for Equality
private:
std::vector<double> v;
};
所以单元测试看起来像这样
/// TEST1 ////////////////
MyTypeWithDouble t1(42.100001);
BOOST_CHECK_CLOSE(t1,42.1,0.1);
//////TEST2//////////////////////////////
std::vector<double> v; //no initalizer do not have c++11 :-(
v.push_back(42.1);
MyTypeWithVector t2(v);
std::vector<double> compare;
compare.push_back(42.100001);
MY_OWN_FUNCTION_USING_BOOST(t2,compare,0.1); //There is only BOOST_CHECK_EQUAL_COLLECTION available for collections
- 我不想更改 '== 运算符的 or 的实现 提供吸气剂。
- 添加其他算子函数即可。
谢谢,ToBe
我认为你设计过度了。我会建议一个简单的宏,也许有一个合适的朋友定义。话虽如此,让我们接受挑战吧。
必要的调整
你的类型应该
- default-constructible 用于
check_is_close_t
实施。 - 此外,必须 有一种获取值的方法,并且由于您拒绝创建 getter,剩下的唯一选择是声明一个访问器class 作为好友
我们得到
class MyTypeWithDouble
{
public:
constexpr MyTypeWithDouble(double v = 0) : m(v) {}
MyTypeWithDouble& operator=(MyTypeWithDouble const&) noexcept = default;
constexpr MyTypeWithDouble(MyTypeWithDouble const&) noexcept = default;
private:
friend class unittest_op::access;
double m;
};
通过一些乏味的工作(在 header?),我们可以利用这个 access
漏洞来实现其他一切。 如何? 好吧,我们定义了一个 "getter",好吧,但是 在 class 定义之外 。
我在 access
中定义了一个特征 class 模板(所以它隐含了一个 friend
),你可以专门针对你的 "floating-point-like" 类型:
namespace unittest_op {
template<> class access::impl<MyTypeWithDouble> {
public:
typedef double result_type;
static result_type call(MyTypeWithDouble const& v) { return v.m; }
};
}
仅此而已。好吧,这就是 你 作为 type/test 实施者的全部内容。当然,我们还需要完成这项工作。
基本要素
存在 unittest_op
命名空间的唯一原因是定义 "relaying" 知道如何访问自定义类型中包含的值的运算符。
请注意我们如何
- 不需要向您的 user-defined 类型添加任何内容
- 我们也得到混合操作数(例如
2 * MyTypeWithDouble(7.0) -> MyTypeWithDouble(14.0)
) - 并且我们还定义了
operator<<
,因此断言宏知道如何打印MyTypeWithDouble
的值
感谢c++11工作并不复杂:
namespace unittest_op {
class access {
template<typename T, typename Enable = void> class impl;
template<typename T>
class impl<T, typename std::enable_if<std::is_arithmetic<T>::value, void>::type>
{
public: typedef T result_type;
static T & call(T& v) { return v; }
static T const& call(T const& v) { return v; }
};
public:
template<typename T>
static typename impl<T>::result_type do_access(T const& v) { return impl<T>::call(v); }
template<typename T> static constexpr bool can_access(decltype(do_access(std::declval<T>()))*) { return true; }
template<typename T> static constexpr bool can_access(...) { return false; }
};
template<typename T>
typename std::enable_if<access::can_access<T>(nullptr) && not std::is_arithmetic<T>::value, std::ostream&>::type
operator<<(std::ostream& os, T const& v) { return os << "{" << access::do_access(v) << "}"; }
template <typename T, typename Enable=decltype(access::do_access(std::declval<T>())) >
static T operator-(T const& lhs) { return - access::do_access(lhs); }
template <typename T, typename Enable=decltype(access::do_access(std::declval<T>())) >
static T operator+(T const& lhs) { return + access::do_access(lhs); }
#define UNITTEST_OP_BINOP(OP) \
template <typename T1, typename T2> \
static decltype(access::do_access(std::declval<T1>()) OP access::do_access(std::declval<T2>())) \
operator OP(T1 const& lhs, T2 const& rhs) { return access::do_access(lhs) OP access::do_access(rhs); } \
using ::unittest_op::operator OP;
UNITTEST_OP_BINOP(==)
UNITTEST_OP_BINOP(!=)
UNITTEST_OP_BINOP(< )
UNITTEST_OP_BINOP(> )
UNITTEST_OP_BINOP(<=)
UNITTEST_OP_BINOP(>=)
UNITTEST_OP_BINOP(+ )
UNITTEST_OP_BINOP(- )
UNITTEST_OP_BINOP(% )
UNITTEST_OP_BINOP(* )
UNITTEST_OP_BINOP(/ )
// assign-ops only for lvalue types (i.e. identity `access::impl<T>`)
UNITTEST_OP_BINOP(+=)
UNITTEST_OP_BINOP(-=)
UNITTEST_OP_BINOP(%=)
UNITTEST_OP_BINOP(*=)
UNITTEST_OP_BINOP(/=)
#undef UNITTEST_OP_BINOP
}
请注意,这些都是 "open" 模板,我们已采取必要的预防措施以确保这些运算符 仅 适用,前提是 do_access
是定义 和 该类型不是算术类型。
为什么要采取这些预防措施?
嗯。我们要做一个 power-move:我们将把运算符重载注入 boost::test_tools
命名空间,以便 BOOST_CHECK*
宏实现可以找到它们。
如果我们不采取刚才提到的预防措施,我们会因我们不关心的类型的模糊运算符重载而引发很多问题。
夺权
权力获取很简单:我们在 boost::test_tools
命名空间中注入 (using
) 我们的每个操作符模板。
现在我们可以开始了:
BOOST_AUTO_TEST_CASE(my_test)
{
MyTypeWithDouble v(4);
BOOST_CHECK_CLOSE(3.99, v, MyTypeWithDouble(0.1));
}
版画
Running 2 test cases...
main.cpp(117): error in "my_test": difference{0.25%} between 3.99{3.9900000000000002} and v{{4}} exceeds {0.10000000000000001}%
完整节目
#include <utility>
#include <type_traits>
#include <iostream>
namespace unittest_op {
class access {
template<typename T, typename Enable = void> class impl;
template<typename T>
class impl<T, typename std::enable_if<std::is_arithmetic<T>::value, void>::type>
{
public: typedef T result_type;
static T & call(T& v) { return v; }
static T const& call(T const& v) { return v; }
};
public:
template<typename T>
static typename impl<T>::result_type do_access(T const& v) { return impl<T>::call(v); }
template<typename T> static constexpr bool can_access(decltype(do_access(std::declval<T>()))*) { return true; }
template<typename T> static constexpr bool can_access(...) { return false; }
};
template<typename T>
typename std::enable_if<access::can_access<T>(nullptr) && not std::is_arithmetic<T>::value, std::ostream&>::type
operator<<(std::ostream& os, T const& v) { return os << "{" << access::do_access(v) << "}"; }
template <typename T, typename Enable=decltype(access::do_access(std::declval<T>())) >
static T operator-(T const& lhs) { return - access::do_access(lhs); }
template <typename T, typename Enable=decltype(access::do_access(std::declval<T>())) >
static T operator+(T const& lhs) { return + access::do_access(lhs); }
#define UNITTEST_OP_BINOP(OP) \
template <typename T1, typename T2> \
static decltype(access::do_access(std::declval<T1>()) OP access::do_access(std::declval<T2>())) \
operator OP(T1 const& lhs, T2 const& rhs) { return access::do_access(lhs) OP access::do_access(rhs); } \
using ::unittest_op::operator OP;
UNITTEST_OP_BINOP(==)
UNITTEST_OP_BINOP(!=)
UNITTEST_OP_BINOP(< )
UNITTEST_OP_BINOP(> )
UNITTEST_OP_BINOP(<=)
UNITTEST_OP_BINOP(>=)
UNITTEST_OP_BINOP(+ )
UNITTEST_OP_BINOP(- )
UNITTEST_OP_BINOP(% )
UNITTEST_OP_BINOP(* )
UNITTEST_OP_BINOP(/ )
// assign-ops only for lvalue types (i.e. identity `access::impl<T>`)
UNITTEST_OP_BINOP(+=)
UNITTEST_OP_BINOP(-=)
UNITTEST_OP_BINOP(%=)
UNITTEST_OP_BINOP(*=)
UNITTEST_OP_BINOP(/=)
#undef UNITTEST_OP_BINOP
}
namespace boost { namespace test_tools {
using unittest_op::operator ==;
using unittest_op::operator !=;
using unittest_op::operator < ;
using unittest_op::operator > ;
using unittest_op::operator <=;
using unittest_op::operator >=;
using unittest_op::operator + ;
using unittest_op::operator - ;
using unittest_op::operator % ;
using unittest_op::operator * ;
using unittest_op::operator / ;
using unittest_op::operator +=;
using unittest_op::operator -=;
using unittest_op::operator %=;
using unittest_op::operator *=;
using unittest_op::operator /=;
using unittest_op::operator <<;
} }
class MyTypeWithDouble
{
public:
constexpr MyTypeWithDouble(double v = 0) : m(v) {}
MyTypeWithDouble& operator=(MyTypeWithDouble const&) noexcept = default;
constexpr MyTypeWithDouble(MyTypeWithDouble const&) noexcept = default;
private:
friend class unittest_op::access;
double m;
};
namespace unittest_op {
template<> class access::impl<MyTypeWithDouble> {
public:
typedef double result_type;
static result_type call(MyTypeWithDouble const& v) { return v.m; }
};
}
#define BOOST_TEST_MODULE MyTest
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_CASE(my_test)
{
MyTypeWithDouble v(4);
BOOST_CHECK_CLOSE(3.99, v, MyTypeWithDouble(0.1));
}
BOOST_AUTO_TEST_CASE(general_operator_invocations) // just a testbed to see the overloads are found and compile
{
MyTypeWithDouble v(4);
using namespace unittest_op; // we're not using the test_tools here
BOOST_CHECK(4.00000000000000001 == v);
BOOST_CHECK(4.000000000000001 != v);
#define UNITTEST_OP_BINOP(OP) { \
auto x = v OP static_cast<MyTypeWithDouble>(0.01); \
x = static_cast<MyTypeWithDouble>(0.01) OP v; \
x = v OP v; \
(void) x; \
}
UNITTEST_OP_BINOP(==)
UNITTEST_OP_BINOP(!=)
UNITTEST_OP_BINOP(+ )
UNITTEST_OP_BINOP(- )
//UNITTEST_OP_BINOP(% )
UNITTEST_OP_BINOP(* )
UNITTEST_OP_BINOP(/ )
UNITTEST_OP_BINOP(< )
UNITTEST_OP_BINOP(> )
UNITTEST_OP_BINOP(<=)
UNITTEST_OP_BINOP(>=)
-v == -v;
+v == +v;
}