catchorg / catchorg/Catch2

Approx for containers, collections, complex data types

Open
#954 0 comments 1 reaction 0 assignees View on GitHub
Dominant language
C++
Stars
21.5k
Forks
3.5k
Avg merge
3d 16h
Merged PRs (30d)
2

Description

* 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 {
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(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::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));
}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.