Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Approx for containers, collections, complex data types #954

Open
tomvierjahn opened this issue Jul 11, 2017 · 0 comments
Open

Approx for containers, collections, complex data types #954

tomvierjahn opened this issue Jul 11, 2017 · 0 comments

Comments

@tomvierjahn
Copy link

  • What about providing an Approx that can be specialised just the same way as StringMaker<> in order to apply it to custom data types.
  • What about providing standard specializations for iterable types?

Pull request #557 aims at a container implementation. However, I think this could/should be generalized.

I came up with the following specific implementation for comapring Qt's QMatrix4x4. Unfortunately, having to initialize base class Approx with a single double is just misleading:

namespace {
class MApprox : public Catch::Detail::Approx {
 public:
  explicit MApprox(const QMatrix4x4& matrix) : Approx(0.0f), matrix_(matrix) {}

  friend bool operator==(const QMatrix4x4& lhs, const MApprox& rhs) {
    bool result = true;
    for (int row = 0; row < 4; ++row) {
      for (int col = 0; col < 4; ++col) {
        result &= (lhs(row, col) == Approx(rhs.matrix_(row, col)));
      }
    }
    return result;
  }
  const QMatrix4x4& Matrix() const { return matrix_; }

 private:
  QMatrix4x4 matrix_;
};
}  // namespace

As a side-note: This is my specialization of StringMaker<>:

namespace Catch {
template <>
struct StringMaker<QMatrix4x4> {
  static std::size_t GetMaxWidth(const QMatrix4x4& matrix) {
    std::size_t max_width = 0;
    for (int row = 0; row < 4; ++row) {
      for (int col = 0; col < 4; ++col) {
        std::ostringstream sstr;
        sstr << matrix(row, col);
        max_width = std::max(max_width, sstr.str().size());
      }
    }
    return max_width;
  }

  static std::string PrettyPrint(const QMatrix4x4& matrix,
                                 std::size_t column_width,
                                 std::size_t indentation) {
    std::ostringstream sstr;
    for (int row = 0; row < 4; ++row) {
      sstr << std::string(indentation, ' ');
      for (int col = 0; col < 4; ++col) {
        sstr << std::setw(static_cast<int>(column_width)) << std::right
             << matrix(row, col);
      }
      sstr << '\n';
    }
    return sstr.str();
  }

  static std::string convert(const QMatrix4x4& matrix) {
    const std::size_t indentation = 2;
    const std::size_t column_separator = 2;
    const std::size_t column_width = GetMaxWidth(matrix) + column_separator;
    return PrettyPrint(matrix, column_width, indentation);
  }
};

template <>
struct StringMaker<::MApprox> {
  static std::string convert(const ::MApprox& approx_matrix) {
    return StringMaker<QMatrix4x4>::convert(approx_matrix.Matrix());
  }
};

And this is the test verifying that MApprox works as desired:

TEST_CASE("Test MApprox", "[MApprox]") {
  const QMatrix4x4 identity(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
                            0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
  const QMatrix4x4 noise(0.1e-5f, -0.2e-5f, 0.2e-5f, -0.1e-5f, -0.3e-5f,
                         0.2e-5f, 0.3e-5f, -0.1e-5f, 0.1e-5f, -0.2e-5f, 0.3e-5f,
                         -0.3e-5f, -0.2e-5f, 0.1e-5f, -0.2e-5f, 0.1e-5f);
  const QMatrix4x4 not_noise(noise * 1.0e3f);
  REQUIRE(identity == MApprox(identity + noise));
  REQUIRE_FALSE(identity == MApprox(identity + not_noise));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant