Boost.TestのBOOST_CHECK_CLOSEの配列バージョンを作った　(タイトルを修正しました)

2015/07/22 修正：タイトルが致命的なまでに間違っていました(内容自体に修正はありません)

# タイトルのとおり
boost.testには、二つの値が等しいかをテストするマクロBOOST_CHECK_EQUALがあります。また、その配列バージョンに、要素ごとに等しいかのテストを行うBOOST_CHECK_EQUAL_COLLECTIONSというマクロもあります。
また、実数型の演算は誤差が発生する可能性があるので、誤差を考慮したうえで、正しい値になっているかを確認する必要があります。その時に使う誤差を許容して比較するマクロ、BOOST_CHECK_CLOSEはありますが、残念なことにその配列バージョンはありません。

ないなら作ればいいじゃないかということで、作りました。

ついでに、配列のbeginとendを指定するのが冗長だったので、そのあたりの処理もマクロにしました。

```cpp
#include <boost/type_traits.hpp>
namespace boost {
namespace test_tools {
namespace tt_detail {
struct close_coll_impl {
  template <typename Left, typename Right, typename ToleranceType>
  assertion_result operator()(Left left_begin, Left left_end, Right right_begin,
                              Right right_end, ToleranceType tolerance) {
    assertion_result pr(true);
    std::size_t pos = 0;

    math::fpc::close_at_tolerance<typename comp_supertype<
        boost::remove_reference<decltype(*Left())>::type,
        boost::remove_reference<decltype(*Right())>::type>::type>
        pred(tolerance, math::fpc::FPC_STRONG);
    for (; left_begin != left_end && right_begin != right_end;
         ++left_begin, ++right_begin, ++pos) {
      if (!pred(*left_begin, *right_begin)) {
        pr = false;
        pr.message() << "\nMismatch at position " << pos << ": "
                     << ::boost::test_tools::tt_detail::print_helper(
                            *left_begin)
                     << " != " << ::boost::test_tools::tt_detail::print_helper(
                                      *right_begin);
      }
    }

    if (left_begin != left_end) {
      std::size_t r_size = pos;
      while (left_begin != left_end) {
        ++pos;
        ++left_begin;
      }

      pr = false;
      pr.message() << "\nCollections size mismatch: " << pos
                   << " != " << r_size;
    }

    if (right_begin != right_end) {
      std::size_t l_size = pos;
      while (right_begin != right_end) {
        ++pos;
        ++right_begin;
      }

      pr = false;
      pr.message() << "\nCollections size mismatch: " << l_size
                   << " != " << pos;
    }

    return pr;
  }
};
}
}
}
//---------------------------------------------------------------------------
#define BOOST_WARN_CLOSE_COLLECTIONS(L_begin, L_end, R_begin, R_end, T)      \
  BOOST_TEST_TOOL_IMPL(1, ::boost::test_tools::tt_detail::close_coll_impl(), \
                       "", WARN, CHECK_EQUAL_COLL,                           \
                       (L_begin)(L_end)(R_begin)(R_end)(                     \
                           ::boost::math::fpc::percent_tolerance(T))) /**/
#define BOOST_CHECK_CLOSE_COLLECTIONS(L_begin, L_end, R_begin, R_end, T)     \
  BOOST_TEST_TOOL_IMPL(1, ::boost::test_tools::tt_detail::close_coll_impl(), \
                       "", CHECK, CHECK_EQUAL_COLL,                          \
                       (L_begin)(L_end)(R_begin)(R_end)(                     \
                           ::boost::math::fpc::percent_tolerance(T))) /**/
#define BOOST_REQUIRE_CLOSE_COLLECTIONS(L_begin, L_end, R_begin, R_end, T)   \
  BOOST_TEST_TOOL_IMPL(1, ::boost::test_tools::tt_detail::close_coll_impl(), \
                       "", REQUIRE, CHECK_EQUAL_COLL,                        \
                       (L_begin)(L_end)(R_begin)(R_end)(                     \
                           ::boost::math::fpc::percent_tolerance(T))) /**/
//---------------------------------------------------------------------------
#define BOOST_WARN_EQUAL_RANGES(left, right)                     \
  BOOST_WARN_EQUAL_COLLECTIONS(std::begin(left), std::end(left), \
                               std::begin(right), std::end(right))
#define BOOST_CHECK_EQUAL_RANGES(left, right)                     \
  BOOST_CHECK_EQUAL_COLLECTIONS(std::begin(left), std::end(left), \
                                std::begin(right), std::end(right))
#define BOOST_REQUIRE_EQUAL_RANGES(left, right)                     \
  BOOST_REQUIRE_EQUAL_COLLECTIONS(std::begin(left), std::end(left), \
                                  std::begin(right), std::end(right))
//---------------------------------------------------------------------------
#define BOOST_WARN_CLOSE_RANGES(left, right, t)                  \
  BOOST_WARN_CLOSE_COLLECTIONS(std::begin(left), std::end(left), \
                               std::begin(right), std::end(right), t)
#define BOOST_CHECK_CLOSE_RANGES(left, right, t)                  \
  BOOST_CHECK_CLOSE_COLLECTIONS(std::begin(left), std::end(left), \
                                std::begin(right), std::end(right), t)
#define BOOST_REQUIRE_CLOSE_RANGES(left, right, t)                  \
  BOOST_REQUIRE_CLOSE_COLLECTIONS(std::begin(left), std::end(left), \
                                  std::begin(right), std::end(right), t)

```

BOOST_CHECK_CLOSEの配列バージョンのマクロはBOOST_CHECK_CLOSE_COLLECTIONSです。
また、配列の範囲の指定を自動化したのが、BOOST_CHECK_CLOSE_RANGESです。
ついでに、BOOST_CHECK_EQUAL_COLLECTIONSの範囲指定を自動化バージョン、BOOST_CHECK_EQUAL_RANGESも作りました。

# 簡単な解説
close_coll_impl構造体は、BOOST_CHECK_EQUAL_COLLECTIONSで利用しているequal_coll_implのほとんどコピーです。二つの値を比較する関数は、BOOST_CHECK_CLOSEのcheck_is_close_t構造体を参考にしました。

値を比較する関数の型にdecltypeを使っているので、古いコンパイラーでは動かない可能性があります。

# 最後に
あまり細かい検証を行っていないので、場合によっては動かないかもしれません。
その時は、よろしければ御一報ください。