Метод equals( boost::any, boost::any ) возвращает пару булевых значений:
- (1) проверка на равенство типов
- (2) проверка собственно самих значений
Сравнение я назвал "ленивым", т.к. если простые типы оказались не равными и хотя бы одно из значений является строкой, делается попытка распознать в строках числа и сравнить их.
Типами метод чрезмерно не нагружал, т.к. каждая проверка - это процессорное время. Вы можете запросто добавить в код свои типы и/или исключить имеющиеся. Ведь код должен эффективно решать поставленную задачу и (в идеале) быть лёгким для понимания. Остальное - от лукавого!
/**
* Сравнивает два значения boost::any.
*
* (!) Может возвращать ( false, true ), т.к. числа разных типов
* сравниваются как числа.
*
* @param precision Точность для сравнения значений с плавающей точкой.
*/
inline std::pair<
// типы совпадают?
bool,
// содержимое совпадает?
bool
> lazyEquals(
const boost::any& a,
const boost::any& b,
double precision = 1e-8
) {
using namespace boost;
// Быстрая оценка на равенство
const type_info& at = a.type();
const type_info& bt = b.type();
std::pair< bool, bool > r = std::make_pair(
(at == bt),
false
);
// double
if (at == typeid( double )) {
const auto ca = any_cast< double >( a );
double cb = numeric_limits< double >::infinity();
if (bt == typeid( double )) {
cb = any_cast< double >( b );
} else if (bt == typeid( float )) {
cb = (double)any_cast< float >( b );
} else if (bt == typeid( int )) {
cb = (double)any_cast< int >( b );
} else if (bt == typeid( size_t )) {
cb = (double)any_cast< size_t >( b );
} else if (bt == typeid( string )) {
cb = numberCast( any_cast< string >( b ) );
// Получим infinity(), если не число
}
if (cb != numeric_limits< double >::infinity()) {
r.second = (precision > std::abs( ca - cb ));
}
// float
} else if (at == typeid( float )) {
const auto ca = any_cast< float >( a );
float cb = numeric_limits< float >::infinity();
if (bt == typeid( double )) {
cb = (float)any_cast< double >( b );
} else if (bt == typeid( float )) {
cb = any_cast< float >( b );
} else if (bt == typeid( int )) {
cb = (float)any_cast< int >( b );
} else if (bt == typeid( size_t )) {
cb = (float)any_cast< size_t >( b );
} else if (bt == typeid( string )) {
cb = (float)numberCast( any_cast< string >( b ) );
// Получим infinity(), если не число
}
if (cb != numeric_limits< float >::infinity()) {
r.second = (precision > std::abs( ca - cb ));
}
// int
} else if (at == typeid( int )) {
const auto ca = any_cast< int >( a );
if (bt == typeid( double )) {
r.second = (ca == (int)any_cast< double >( b ));
} else if (bt == typeid( float )) {
r.second = (ca == any_cast< float >( b ));
} else if (bt == typeid( int )) {
r.second = (ca == any_cast< int >( b ));
} else if (bt == typeid( size_t )) {
r.second = (ca == (int)any_cast< size_t >( b ));
} else if (bt == typeid( string )) {
const double temp = numberCast( any_cast< string >( b ) );
// Получим infinity(), если не число
if (temp != numeric_limits< double >::infinity()) {
r.second = (ca == (int)temp);
}
}
// size_t
} else if (at == typeid( size_t )) {
const auto ca = any_cast< size_t >( a );
if (bt == typeid( double )) {
r.second = (ca == (size_t)any_cast< double >( b ));
} else if (bt == typeid( float )) {
r.second = (ca == (size_t)any_cast< float >( b ));
} else if (bt == typeid( int )) {
r.second = (ca == (size_t)any_cast< int >( b ));
} else if (bt == typeid( size_t )) {
r.second = (ca == any_cast< size_t >( b ));
} else if (bt == typeid( string )) {
const double temp = numberCast( any_cast< string >( b ) );
// Получим infinity(), если не число
if (temp != numeric_limits< double >::infinity()) {
r.second = (ca == (size_t)temp);
}
}
// string
} else if (at == typeid( string )) {
const string ca = any_cast< string >( a );
if (bt == typeid( string )) {
r.second = (ca == any_cast< string >( b ));
}
// Может, число представлено строкой? Строки переведём
// в числа и сравним числа согласно их типу.
const double nca = numberCast( ca );
if (nca != numeric_limits< double >::infinity()) {
if (bt == typeid( double )) {
const auto ncb = any_cast< double >( b );
r.second = (precision > std::abs( nca - ncb ));
} else if (bt == typeid( float )) {
const auto ncb = any_cast< float >( b );
r.second = (precision > std::abs( (float)nca - ncb ));
} else if (bt == typeid( int )) {
const auto ncb = any_cast< int >( b );
r.second = ((int)nca == ncb);
} else if (bt == typeid( size_t )) {
const auto ncb = any_cast< size_t >( b );
r.second = ((size_t)nca == ncb);
}
// string посмотрели выше
}
} // if-else ...
return r;
}
В методе используется вот такая функция, которая
/**
* @return Распознанное число или numeric_limits< double >::infinity().
*/
inline double numberCast( const string& s ) {
try {
const double number = boost::lexical_cast< double >( s );
return number;
}
catch ( boost::bad_lexical_cast& ) {
return numeric_limits< double >::infinity();
}
}
И, конечно же, тесты! Без них любой код смотрится подозрительно. Используем googletest. Надо говорить, что все t. - зелёные?
/**
* Фикстура.
*/
class DTest : public ::testing::Test {
protected:
inline DTest() {
}
virtual inline ~DTest() {
}
virtual inline void SetUp() {
}
virtual void TearDown() {
}
};
Одинаковые типы данных
TEST_F( DTest, IntEqualsInt_LazyEquals ) {
const auto a = boost::any( (int)100 );
const auto b = boost::any( (int)100 );
const auto r = d::lazyEquals( a, b );
EXPECT_TRUE( r.first );
EXPECT_TRUE( r.second );
}
TEST_F( DTest, IntNotEqualsInt_LazyEquals ) {
const auto a = boost::any( (int)100 );
const auto b = boost::any( (int)200 );
const auto r = d::lazyEquals( a, b );
EXPECT_TRUE( r.first );
EXPECT_FALSE( r.second );
}
TEST_F( DTest, SizeTEqualsSizeT_LazyEquals ) {
const auto a = boost::any( (size_t)100 );
const auto b = boost::any( (size_t)100 );
const auto r = d::lazyEquals( a, b );
EXPECT_TRUE( r.first );
EXPECT_TRUE( r.second );
}
TEST_F( DTest, SizeTNotEqualsSizeT_LazyEquals ) {
const auto a = boost::any( (size_t)100 );
const auto b = boost::any( (size_t)200 );
const auto r = d::lazyEquals( a, b );
EXPECT_TRUE( r.first );
EXPECT_FALSE( r.second );
}
TEST_F( DTest, FloatEqualsFloat_LazyEquals ) {
const auto a = boost::any( (float)100 );
const auto b = boost::any( (float)100 );
const auto r = d::lazyEquals( a, b );
EXPECT_TRUE( r.first );
EXPECT_TRUE( r.second );
}
TEST_F( DTest, FloatNotEqualsFloat_LazyEquals ) {
const auto a = boost::any( (float)100 );
const auto b = boost::any( (float)200 );
const auto r = d::lazyEquals( a, b );
EXPECT_TRUE( r.first );
EXPECT_FALSE( r.second );
}
TEST_F( DTest, DoubleEqualsDouble_LazyEquals ) {
const auto a = boost::any( (double)100 );
const auto b = boost::any( (double)100 );
const auto r = d::lazyEquals( a, b );
EXPECT_TRUE( r.first );
EXPECT_TRUE( r.second );
}
TEST_F( DTest, DoubleNotEqualsDouble_LazyEquals ) {
const auto a = boost::any( (double)100 );
const auto b = boost::any( (double)200 );
const auto r = d::lazyEquals( a, b );
EXPECT_TRUE( r.first );
EXPECT_FALSE( r.second );
}
TEST_F( DTest, StringLatinEqualsStringLatin_LazyEquals ) {
const auto a = boost::any( (string)"abcd" );
const auto b = boost::any( (string)"abcd" );
const auto r = d::lazyEquals( a, b );
EXPECT_TRUE( r.first );
EXPECT_TRUE( r.second );
}
TEST_F( DTest, StringLatinNotEqualsStringLatin_LazyEquals ) {
const auto a = boost::any( (string)"abcd" );
const auto b = boost::any( (string)"abcdefgh" );
const auto r = d::lazyEquals( a, b );
EXPECT_TRUE( r.first );
EXPECT_FALSE( r.second );
}
TEST_F( DTest, StringUnicodeEqualsStringUnicode_LazyEquals ) {
const auto a = boost::any( (string)"Юникод" );
const auto b = boost::any( (string)"Юникод" );
const auto r = d::lazyEquals( a, b );
EXPECT_TRUE( r.first );
EXPECT_TRUE( r.second );
}
TEST_F( DTest, StringUnicodeNotEqualsStringUnicode_LazyEquals ) {
const auto a = boost::any( (string)"Юникод" );
const auto b = boost::any( (string)"Юникод тоже" );
const auto r = d::lazyEquals( a, b );
EXPECT_TRUE( r.first );
EXPECT_FALSE( r.second );
}
Разные типы данных
TEST_F( DTest, IntEqualsSizeT_LazyEquals ) {
auto a = boost::any( (int)100 );
auto b = boost::any( (size_t)100 );
auto r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (size_t)100 );
b = boost::any( (int)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
}
TEST_F( DTest, IntNotEqualsSizeT_LazyEquals ) {
auto a = boost::any( (int)100 );
auto b = boost::any( (size_t)200 );
auto r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (size_t)100 );
b = boost::any( (int)200 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
}
TEST_F( DTest, IntEqualsFloat_LazyEquals ) {
auto a = boost::any( (int)100 );
auto b = boost::any( (float)100 );
auto r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (float)100 );
b = boost::any( (int)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
}
TEST_F( DTest, IntNotEqualsFloat_LazyEquals ) {
auto a = boost::any( (int)100 );
auto b = boost::any( (float)200 );
auto r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (float)100 );
b = boost::any( (int)200 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
}
TEST_F( DTest, IntEqualsDouble_LazyEquals ) {
auto a = boost::any( (int)100 );
auto b = boost::any( (double)100 );
auto r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (double)100 );
b = boost::any( (int)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
}
TEST_F( DTest, IntNotEqualsDouble_LazyEquals ) {
auto a = boost::any( (int)100 );
auto b = boost::any( (double)200 );
auto r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (double)100 );
b = boost::any( (int)200 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
}
Строки - Равенство
TEST_F( DTest, NumberEqualsString_LazyEquals ) {
// int
auto a = boost::any( (int)100 );
auto b = boost::any( (string)"100" );
auto r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (string)"100" );
b = boost::any( (int)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (int)100 );
b = boost::any( (string)"100.0" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (string)"100.0" );
b = boost::any( (int)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
// size_t
a = boost::any( (size_t)100 );
b = boost::any( (string)"100" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (string)"100" );
b = boost::any( (size_t)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (size_t)100 );
b = boost::any( (string)"100.0" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (string)"100.0" );
b = boost::any( (size_t)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (size_t)100 );
b = boost::any( (string)"100.1" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (string)"100.1" );
b = boost::any( (size_t)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
// float
a = boost::any( (float)100 );
b = boost::any( (string)"100" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (string)"100" );
b = boost::any( (float)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (float)100 );
b = boost::any( (string)"100.0" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (string)"100.0" );
b = boost::any( (float)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (float)100.1 );
b = boost::any( (string)"100.1" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (string)"100.1" );
b = boost::any( (float)100.1 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
// double
a = boost::any( (double)100 );
b = boost::any( (string)"100" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (string)"100" );
b = boost::any( (double)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (double)100 );
b = boost::any( (string)"100.0" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (string)"100.0" );
b = boost::any( (double)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (double)100.1 );
b = boost::any( (string)"100.1" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
a = boost::any( (string)"100.1" );
b = boost::any( (double)100.1 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_TRUE( r.second );
}
Строки - Не равенство
TEST_F( DTest, NumberNotEqualsString2_LazyEquals ) {
// int
auto a = boost::any( (int)100 );
auto b = boost::any( (string)"200" );
auto r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (string)"200" );
b = boost::any( (int)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (int)100 );
b = boost::any( (string)"200.0" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (string)"200.0" );
b = boost::any( (int)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
// size_t
a = boost::any( (size_t)100 );
b = boost::any( (string)"200" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (string)"200" );
b = boost::any( (size_t)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (size_t)100 );
b = boost::any( (string)"200.0" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (string)"200.0" );
b = boost::any( (size_t)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
// float
a = boost::any( (float)100 );
b = boost::any( (string)"200" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (string)"200" );
b = boost::any( (float)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (float)100 );
b = boost::any( (string)"200.0" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (string)"200.0" );
b = boost::any( (float)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
// double
a = boost::any( (double)100 );
b = boost::any( (string)"200" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (string)"200" );
b = boost::any( (double)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (double)100 );
b = boost::any( (string)"200.0" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (string)"200.0" );
b = boost::any( (double)100 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
}
TEST_F( DTest, Number2NotEqualsString_LazyEquals ) {
// int
auto a = boost::any( (int)200 );
auto b = boost::any( (string)"100" );
auto r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (string)"100" );
b = boost::any( (int)200 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (int)200 );
b = boost::any( (string)"100.0" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (string)"100.0" );
b = boost::any( (int)200 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
// size_t
a = boost::any( (size_t)200 );
b = boost::any( (string)"100" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (string)"100" );
b = boost::any( (size_t)200 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (size_t)200 );
b = boost::any( (string)"100.0" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (string)"100.0" );
b = boost::any( (size_t)200 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
// float
a = boost::any( (float)200 );
b = boost::any( (string)"100" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (string)"100" );
b = boost::any( (float)200 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (float)200 );
b = boost::any( (string)"100.0" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (string)"100.0" );
b = boost::any( (float)200 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
// double
a = boost::any( (double)200 );
b = boost::any( (string)"100" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (string)"100" );
b = boost::any( (double)200 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (double)200 );
b = boost::any( (string)"100.0" );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
a = boost::any( (string)"100.0" );
b = boost::any( (double)200 );
r = d::lazyEquals( a, b );
EXPECT_FALSE( r.first );
EXPECT_FALSE( r.second );
}
Комментариев нет:
Отправить комментарий