diff --git a/aoc_lib/src/ds/grid.hpp b/aoc_lib/src/ds/grid.hpp index ab61897..57c8243 100644 --- a/aoc_lib/src/ds/grid.hpp +++ b/aoc_lib/src/ds/grid.hpp @@ -6,17 +6,18 @@ #include "util/concepts.hpp" // for any_convertible_range #include // for copy, move +#include // for array #include // for assert #include // for partial_ordering #include // for same_as, convertible_to -#include // for size_t, ptrdiff_t +#include // for abs, size_t, ptrdiff_t #include // for setw #include // for ostream #include // for begin, end, back_inserter, misc concepts #include // for range, range_value_t #include // for span, dynamic_extent // IWYU pragma: export #include // for string, basic_string -#include // for is_same_v, conditional_t, is_rvalue_reference_v // IWYU pragma: export +#include // for conditional_t, is_rvalue_reference_v // IWYU pragma: export #include // for move, pair, make_pair #include // for vector, __cpp_lib_constexpr_vector @@ -195,17 +196,19 @@ struct Grid { container_type m_data; public: + // construct with the same value everywhere constexpr Grid(size_type width, size_type height, const value_type &value = value_type()) : height(height), width(width), m_data(height * width, value) {} - // construct from a flat range + // construct from a flat range (note: not compatible with CTAD) template R> constexpr Grid(size_type width, size_type height, R &&range) : height(height), width(width), m_data(std::begin(range), std::end(range)) { assert(static_cast(m_data.size()) == height * width); } - // move-construct from a flat data container + // move-construct from a flat data container (note: not compatible with + // CTAD) constexpr Grid(size_type width, size_type height, container_type &&data) : height(height), width(width), m_data(std::move(data)) { assert(static_cast(m_data.size()) == height * width); @@ -458,9 +461,9 @@ std::ostream &print_repr(std::ostream &os, const aoc::ds::Grid &grid, return os; } -// instantiate templates in an anonymous namespace, so static analyzers will -// check these functions -namespace { +// instantiate templates in a non-templated helper function, so static +// analyzers will check these functions +namespace test { #if __cpp_lib_constexpr_vector constexpr bool _grid_lint_helper_constexpr() { Grid grid1(5, 10, 0); @@ -473,41 +476,86 @@ constexpr bool _grid_lint_helper_constexpr() { } #endif [[maybe_unused]] void _grid_lint_helper() { - Grid grid1a(10, 5); - Grid grid1b(10, 5, 10); - static_assert(std::is_same_v); + // reduce boilerplate + constexpr auto same_type = [](const auto &a, const auto &b) { + return std::same_as; + }; + // construct with the same value everywhere + { + Grid int_grid(10, 5); + Grid int_grid_2(10, 5, 10); + static_assert(same_type(int_grid, int_grid_2)); - Grid> grid2a(3, 2); - Grid grid2b(3, 2, std::make_pair(1, std::string{"a"})); - static_assert(std::is_same_v); + Grid> pair_grid_1(3, 2); + Grid pair_grid_2(3, 2, std::make_pair(1, std::string{"a"})); + static_assert(same_type(pair_grid_1, pair_grid_2)); + } - std::vector> bool_vec{{true, false}, {true, false}}; - Grid grid3a(bool_vec); - Grid grid3b(bool_vec); - Grid grid3c(std::move(bool_vec)); - static_assert(std::is_same_v); - static_assert(std::is_same_v); + // construct from a flat range + { + std::array flat_arr; + Grid grid_1(2, 5, flat_arr); + static_assert( + std::same_as); + std::vector flat_vec(10, 3.14f); + Grid grid_2(2, 5, flat_vec); + static_assert( + std::same_as); + static_assert(same_type(grid_1, grid_2)); + } - [[maybe_unused]] Grid grid4a(grid1a, 0.0); - [[maybe_unused]] Grid grid4b(grid1a, 1); + // move-construct from a flat data container + { + std::vector data(15, "foobar"); + Grid grid(3, 5, std::move(data)); + static_assert( + std::same_as); + } - static_assert(std::bidirectional_iterator::iterator>); - static_assert(!std::random_access_iterator::iterator>); - static_assert(!std::contiguous_iterator::iterator>); + // construct from nested ranges + { + std::vector> bool_vec{{true, false}, {true, false}}; + Grid bool_grid_1(bool_vec); + Grid bool_grid_2(bool_vec); + Grid bool_grid_3(std::move(bool_vec)); + static_assert(same_type(bool_grid_1, bool_grid_2)); + static_assert(same_type(bool_grid_1, bool_grid_3)); + } - static_assert(std::bidirectional_iterator::iterator>); - static_assert(!std::random_access_iterator::iterator>); - static_assert(!std::contiguous_iterator::iterator>); + // construct with the same dimensions as another grid + { + Grid grid_1(3, 4); + Grid grid_2(grid_1, 0.0); + assert(grid_1.width == grid_2.width && grid_1.height == grid_2.height); + Grid grid_3(grid_2, 1); + assert(grid_1.width == grid_3.width && grid_1.height == grid_3.height); + } + // check that iterators satisfy the expected constraints + { + constexpr auto check_iterator = []() { + static_assert(std::bidirectional_iterator); + static_assert(!std::random_access_iterator); + static_assert(!std::contiguous_iterator); + }; + check_iterator.template operator()::iterator>(); + check_iterator.template operator()::const_iterator>(); + check_iterator.template operator()::iterator>(); + check_iterator.template operator()::const_iterator>(); + } + + // basic iterator test { + Grid grid(5, 7, 1); int i = 0; int y = 0; - for (auto &row : grid1a) { + for (auto &row : grid) { int x = 0; for (auto &val : row) { - val = i++; - assert(grid1a.at(x, y) == i); + val = i; + assert(grid.at(x, y) == i); ++x; + ++i; } ++y; } @@ -532,7 +580,7 @@ constexpr bool _grid_lint_helper_constexpr() { static_assert(_grid_lint_helper_constexpr()); #endif } -} // namespace +} // namespace test } // namespace aoc::ds diff --git a/aoc_lib/src/ds/pairing_heap.hpp b/aoc_lib/src/ds/pairing_heap.hpp index 5c6ef1f..bbb79f4 100644 --- a/aoc_lib/src/ds/pairing_heap.hpp +++ b/aoc_lib/src/ds/pairing_heap.hpp @@ -290,9 +290,9 @@ pairing_heap(InputIt, InputIt, const Compare & = Compare()) -> pairing_heap::value_type, Compare>; -// instantiate templates in an anonymous namespace, so static analyzers will -// check these functions -namespace { +// instantiate templates in a non-templated helper function, so static +// analyzers will check these functions +namespace test { [[maybe_unused]] void _pairing_heap_lint_helper() { // default constructor pairing_heap h1a; @@ -312,7 +312,7 @@ namespace { pairing_heap, std::greater>> h4; } -} // namespace +} // namespace test } // namespace aoc::ds diff --git a/aoc_lib/src/test_ds.cpp b/aoc_lib/src/test_ds.cpp index 94c623d..aa81239 100644 --- a/aoc_lib/src/test_ds.cpp +++ b/aoc_lib/src/test_ds.cpp @@ -226,5 +226,8 @@ int main() { failed_count += aoc::ds::test::test_grid(); failed_count += aoc::ds::test::test_grid(); failed_count += aoc::ds::test::test_grid_repr(); + // run linter helper functions, to catch any failed asserts + aoc::ds::test::_pairing_heap_lint_helper(); + aoc::ds::test::_grid_lint_helper(); return unit_test::fix_exit_code(failed_count); }