From 73ed773e8687ca5d4af581d4a3c4c8ba1b61e706 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Fri, 3 Apr 2026 20:54:32 +0200 Subject: [PATCH 1/7] no i dupa - ten sam durny false positive z array | join --- .clangd | 1 - include/gl/graph.hpp | 3 +- include/gl/impl/adjacency_list.hpp | 2 +- include/gl/topology/cycle.hpp | 4 +- include/hgl/conversion.hpp | 6 ++- include/hgl/impl/flat_incidence_list.hpp | 20 +++++----- include/hgl/impl/incidence_list.hpp | 20 +++++----- include/hgl/impl/incidence_matrix.hpp | 26 ++++++------ tests/CMakeLists.txt | 23 ++++++----- tests/include/testing/common/wrnsup.hpp | 18 +++++++++ tests/source/gl/test_adjacency_list.cpp | 2 - tests/source/gl/test_adjacency_matrix.cpp | 9 +---- tests/source/gl/test_alg_bfs.cpp | 1 - tests/source/gl/test_alg_dijkstra.cpp | 11 ++--- tests/source/gl/test_alg_prim_mst.cpp | 8 ++-- tests/source/gl/test_flat_jagged_vector.cpp | 7 +++- tests/source/gl/test_flat_matrix.cpp | 7 +++- tests/source/gl/test_graph.cpp | 16 ++++---- .../gl/test_graph_topology_builders.cpp | 23 +++++------ .../source/gl/test_vertex_degree_getters.cpp | 2 - tests/source/hgl/test_flat_incidence_list.cpp | 40 +++++++++---------- 21 files changed, 135 insertions(+), 114 deletions(-) create mode 100644 tests/include/testing/common/wrnsup.hpp diff --git a/.clangd b/.clangd index 0546aea..64850f3 100644 --- a/.clangd +++ b/.clangd @@ -8,7 +8,6 @@ CompileFlags: - -Wextra - -Wcast-align - -Wconversion - - -Wsign-conversion - -Wunreachable-code - -Wuninitialized - -Wunused diff --git a/include/gl/graph.hpp b/include/gl/graph.hpp index a9b2d91..15349ed 100644 --- a/include/gl/graph.hpp +++ b/include/gl/graph.hpp @@ -9,6 +9,7 @@ #include "gl/impl/impl_tags.hpp" #include "gl/io/stream_options_manipulator.hpp" #include "gl/util/ranges.hpp" +#include #include @@ -212,7 +213,7 @@ class graph final { } } - gl_attr_force_inline void remove_vertex(const size_type vertex_id) { + gl_attr_force_inline void remove_vertex(const id_type vertex_id) { this->_verify_vertex_id(vertex_id); this->_remove_vertex_impl(vertex_id); } diff --git a/include/gl/impl/adjacency_list.hpp b/include/gl/impl/adjacency_list.hpp index 72a8cd8..3662ec3 100644 --- a/include/gl/impl/adjacency_list.hpp +++ b/include/gl/impl/adjacency_list.hpp @@ -297,7 +297,7 @@ class adjacency_list final { edge_item.edge_id = invalid_id; // edge was removed else // shift by the number of removed IDs < edge-id - edge_item.edge_id -= std::ranges::distance(removed_edge_ids.begin(), it); + edge_item.edge_id -= static_cast(it - removed_edge_ids.begin()); // align the vertex id edge_item.vertex_id -= edge_item.vertex_id > removed_vertex_id; diff --git a/include/gl/topology/cycle.hpp b/include/gl/topology/cycle.hpp index 8f5e226..638aebc 100644 --- a/include/gl/topology/cycle.hpp +++ b/include/gl/topology/cycle.hpp @@ -17,7 +17,7 @@ template GraphType graph{n_vertices}; for (id_type source_id = initial_id; source_id < n_vertices; ++source_id) - graph.add_edge(source_id, (source_id + 1uz) % n_vertices); + graph.add_edge(source_id, static_cast((source_id + 1uz) % n_vertices)); return graph; } @@ -36,7 +36,7 @@ template GraphType graph{n_vertices}; for (id_type source_id = initial_id; source_id < n_vertices; ++source_id) { - const auto target_id = (source_id + 1uz) % n_vertices; + const auto target_id = static_cast((source_id + 1uz) % n_vertices); graph.add_edge(source_id, target_id); graph.add_edge(target_id, source_id); } diff --git a/include/hgl/conversion.hpp b/include/hgl/conversion.hpp index 1803cd2..c8b2b21 100644 --- a/include/hgl/conversion.hpp +++ b/include/hgl/conversion.hpp @@ -294,8 +294,10 @@ requires std::same_as [[nodiscard]] G incidence_graph(const traits::c_undirected_hypergraph auto& h) { + using g_id_type = typename G::id_type; + G g{h.order() + h.size()}; - const auto align_edge_id = [shift = h.order()](const auto eid) -> gl::default_id_type { + const auto align_edge_id = [shift = static_cast(h.order())](const auto eid) -> g_id_type { return eid + shift; }; @@ -321,7 +323,7 @@ template using g_id_type = typename G::id_type; G g{h.order() + h.size()}; - const auto align_edge_id = [shift = h.order()](const auto eid) -> gl::default_id_type { + const auto align_edge_id = [shift = static_cast(h.order())](const auto eid) -> g_id_type { return eid + shift; }; diff --git a/include/hgl/impl/flat_incidence_list.hpp b/include/hgl/impl/flat_incidence_list.hpp index a57da36..ca2eec0 100644 --- a/include/hgl/impl/flat_incidence_list.hpp +++ b/include/hgl/impl/flat_incidence_list.hpp @@ -237,9 +237,9 @@ class flat_incidence_list final { return this->_storage[to_idx(id)]; } else { // incident with minor - return std::views::iota(initial_id_v, this->_storage.size()) - | std::views::filter([this, minor_id = id](size_type major_idx) { - return detail::contains(this->_storage[major_idx], minor_id); + return std::views::iota(initial_id_v, this->_storage.size()) + | std::views::filter([this, minor_id = id](id_type major_id) { + return detail::contains(this->_storage[to_idx(major_id)], minor_id); }); } } @@ -551,10 +551,10 @@ class flat_incidence_list final { | std::views::join; } else { // get minor - return std::views::iota(initial_id_v, this->_tail_storage.size()) - | std::views::filter([this, minor_id = id](size_type major_idx) { - return detail::contains(this->_tail_storage[major_idx], minor_id) - or detail::contains(this->_head_storage[major_idx], minor_id); + return std::views::iota(initial_id_v, this->_tail_storage.size()) + | std::views::filter([this, minor_id = id](id_type major_id) { + return detail::contains(this->_tail_storage[to_idx(major_id)], minor_id) + or detail::contains(this->_head_storage[to_idx(major_id)], minor_id); }); } } @@ -566,11 +566,11 @@ class flat_incidence_list final { return std::invoke(storage_proj, this)[to_idx(id)]; } else { // get minor - return std::views::iota(initial_id_v, this->_tail_storage.size()) + return std::views::iota(initial_id_v, this->_tail_storage.size()) | std::views::filter( [this, &storage = std::invoke(storage_proj, this), minor_id = id]( - size_type major_idx - ) { return detail::contains(storage[major_idx], minor_id); } + id_type major_id + ) { return detail::contains(storage[to_idx(major_id)], minor_id); } ); } } diff --git a/include/hgl/impl/incidence_list.hpp b/include/hgl/impl/incidence_list.hpp index 530a9ec..6dc8373 100644 --- a/include/hgl/impl/incidence_list.hpp +++ b/include/hgl/impl/incidence_list.hpp @@ -190,9 +190,9 @@ class incidence_list final { return std::views::all(this->_major_storage[to_idx(id)]); } else { // incident with minor - return std::views::iota(initial_id_v, this->_major_storage.size()) - | std::views::filter([this, minor_id = id](size_type major_idx) { - return this->_contains(this->_major_storage[major_idx], minor_id); + return std::views::iota(initial_id_v, this->_major_storage.size()) + | std::views::filter([this, minor_id = id](id_type major_id) { + return this->_contains(this->_major_storage[to_idx(major_id)], minor_id); }); } } @@ -487,10 +487,10 @@ class incidence_list final { | std::views::join; } else { // get minor - return std::views::iota(initial_id_v, this->_tail_storage.size()) - | std::views::filter([this, minor_id = id](size_type major_idx) { - return this->_contains(this->_tail_storage[major_idx], minor_id) - or this->_contains(this->_head_storage[major_idx], minor_id); + return std::views::iota(initial_id_v, this->_tail_storage.size()) + | std::views::filter([this, minor_id = id](id_type major_id) { + return this->_contains(this->_tail_storage[to_idx(major_id)], minor_id) + or this->_contains(this->_head_storage[to_idx(major_id)], minor_id); }); } } @@ -502,11 +502,11 @@ class incidence_list final { return std::views::all(std::invoke(storage_proj, this)[to_idx(id)]); } else { // get minor - return std::views::iota(initial_id_v, this->_tail_storage.size()) + return std::views::iota(initial_id_v, this->_tail_storage.size()) | std::views::filter( [this, &storage = std::invoke(storage_proj, this), minor_id = id]( - size_type major_idx - ) { return this->_contains(storage[major_idx], minor_id); } + id_type major_id + ) { return this->_contains(storage[to_idx(major_id)], minor_id); } ); } } diff --git a/include/hgl/impl/incidence_matrix.hpp b/include/hgl/impl/incidence_matrix.hpp index 613b951..e13e717 100644 --- a/include/hgl/impl/incidence_matrix.hpp +++ b/include/hgl/impl/incidence_matrix.hpp @@ -4,6 +4,7 @@ #pragma once +#include "gl/types/core.hpp" #include "hgl/constants.hpp" #include "hgl/decl/impl_tags.hpp" #include "hgl/directional_tags.hpp" @@ -181,14 +182,13 @@ class incidence_matrix final { template gl_attr_force_inline auto _incident_with(const id_type id) const noexcept { if constexpr (Element == layout_tag::major_element) { // incident with major - return std::views::iota(initial_id_v, this->_matrix_row_size) - | std::views::filter([&row = this->_matrix[to_idx(id)]](const size_type minor_idx - ) { return row[minor_idx]; }); + return std::views::iota(initial_id_v, this->_matrix_row_size) + | std::views::filter([&row = this->_matrix[to_idx(id)]](id_type minor_id) { return row[to_idx(minor_id)]; }); } else { // incident with minor - return std::views::iota(initial_id_v, this->_matrix.size()) - | std::views::filter([this, minor_idx = to_idx(id)](const size_type major_idx) { - return this->_matrix[major_idx][minor_idx]; + return std::views::iota(initial_id_v, this->_matrix.size()) + | std::views::filter([this, minor_idx = to_idx(id)](id_type major_id) { + return this->_matrix[to_idx(major_id)][minor_idx]; }); } } @@ -479,16 +479,16 @@ class incidence_matrix final { const id_type id, std::predicate auto&& pred ) const noexcept { if constexpr (Element == layout_tag::major_element) { // query major - return std::views::iota(initial_id_v, this->_matrix_row_size) - | std::views::filter([&row = this->_matrix[to_idx(id)], - pred](const size_type minor_idx) { - return pred(row[minor_idx]); + return std::views::iota(initial_id_v, this->_matrix_row_size) + | std::views::filter([&row = this->_matrix[to_idx(id)], pred](id_type minor_id) { + return pred(row[to_idx(minor_id)]); }); } else { // query minor - return std::views::iota(initial_id_v, this->_matrix.size()) - | std::views::filter([this, minor_idx = to_idx(id), pred](const size_type major_idx - ) { return pred(this->_matrix[major_idx][minor_idx]); }); + return std::views::iota(initial_id_v, this->_matrix.size()) + | std::views::filter([this, minor_idx = to_idx(id), pred](id_type major_id) { + return pred(this->_matrix[to_idx(major_id)][minor_idx]); + }); } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5ddb372..d116409 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,15 +1,6 @@ cmake_minimum_required(VERSION 3.12) project(cpp-gl-test) -# Set compile options -if(NOT DEFINED CMAKE_CXX_FLAGS) - set( - CMAKE_CXX_FLAGS - "-Werror -Wall -Wextra -Wcast-align -Wconversion -Wsign-conversion -Wunreachable-code -Wuninitialized -Wunused -pedantic -g -O2" - CACHE STRING "Default C++ compile flags" FORCE - ) -endif() - # Common structure set(SOURCE_DIRS "app") set(INCLUDE_DIRS "include" "external") @@ -33,7 +24,19 @@ function(add_test_target TARGET_NAME SOURCE_DIRS DATA_DIR TESTING_DEF) ) target_include_directories(${TARGET_NAME} PRIVATE ${INCLUDE_DIRS}) - target_compile_options(${TARGET_NAME} PRIVATE ${CMAKE_CXX_FLAGS}) + target_compile_options(${TARGET_NAME} PRIVATE + -Werror + -Wall + -Wextra + -Wcast-align + -Wconversion + -Wunreachable-code + -Wuninitialized + -Wunused + -pedantic + -g + -O2 + ) target_compile_definitions(${TARGET_NAME} PRIVATE ${TESTING_DEF} TEST_DATA_PATH="${DATA_DIR}" diff --git a/tests/include/testing/common/wrnsup.hpp b/tests/include/testing/common/wrnsup.hpp new file mode 100644 index 0000000..b48ac5e --- /dev/null +++ b/tests/include/testing/common/wrnsup.hpp @@ -0,0 +1,18 @@ +#pragma once + +#if defined(__clang__) || defined(__GNUC__) + +#define SUPPRESS_PRAGMA(x) _Pragma(#x) + +#define SUPPRESS_WARNING_BEGIN(w) \ + SUPPRESS_PRAGMA(GCC diagnostic push) \ + SUPPRESS_PRAGMA(GCC diagnostic ignored w) + +#define SUPPRESS_WARNING_END SUPPRESS_PRAGMA(GCC diagnostic pop) + +#else + +#define SUPPRESS_WARNING_BEGIN(w) +#define SUPPRESS_WARNING_END + +#endif diff --git a/tests/source/gl/test_adjacency_list.cpp b/tests/source/gl/test_adjacency_list.cpp index 399064f..e4596f9 100644 --- a/tests/source/gl/test_adjacency_list.cpp +++ b/tests/source/gl/test_adjacency_list.cpp @@ -149,7 +149,6 @@ struct test_directed_adjacency_list : public test_adjacency_list { TEST_CASE_TEMPLATE_DEFINE("directed adjacency list tests", SutType, directed_adj_list_template) { using fixture_type = test_directed_adjacency_list; - using sut_type = typename fixture_type::sut_type; using edge_type = typename fixture_type::edge_type; fixture_type fixture; @@ -450,7 +449,6 @@ struct test_undirected_adjacency_list : public test_adjacency_list { TEST_CASE_TEMPLATE_DEFINE("undirected adjacency list tests", SutType, undirected_adj_list_template) { using fixture_type = test_undirected_adjacency_list; - using sut_type = typename fixture_type::sut_type; using edge_type = typename fixture_type::edge_type; fixture_type fixture; diff --git a/tests/source/gl/test_adjacency_matrix.cpp b/tests/source/gl/test_adjacency_matrix.cpp index 166805b..acb0422 100644 --- a/tests/source/gl/test_adjacency_matrix.cpp +++ b/tests/source/gl/test_adjacency_matrix.cpp @@ -74,8 +74,6 @@ TEST_CASE_TEMPLATE_DEFINE( } SUBCASE("add_edge should throw an error if the vertices are already incident") { - using edge_type = typename SutType::edge_type; - SutType sut{constants::n_elements}; sut.add_edge(fixture.next_edge_id++, constants::v1_id, constants::v2_id); REQUIRE(sut.has_edge(constants::v1_id, constants::v2_id)); @@ -87,8 +85,6 @@ TEST_CASE_TEMPLATE_DEFINE( } SUBCASE("add_edges_from should throw an error if the vertices are already incident") { - using edge_type = typename SutType::edge_type; - SutType sut{constants::n_elements}; const auto target_ids = {constants::v1_id, constants::v2_id, constants::v3_id}; @@ -197,7 +193,6 @@ struct test_directed_adjacency_matrix : public test_adjacency_matrix { TEST_CASE_TEMPLATE_DEFINE("directed adjacency matrix tests", SutType, directed_adj_matrix_template) { using fixture_type = test_directed_adjacency_matrix; - using sut_type = typename fixture_type::sut_type; using edge_type = typename fixture_type::edge_type; fixture_type fixture; @@ -230,7 +225,8 @@ TEST_CASE_TEMPLATE_DEFINE("directed adjacency matrix tests", SutType, directed_a SUBCASE("at should return a view equivalent to the matrix row of the given vertex") { for (const auto vertex_id : std::views::iota(constants::v1_id, constants::n_elements)) { - const auto edge = add_edge(vertex_id, (vertex_id + 1u) % constants::n_elements); + const auto target_id = static_cast((vertex_id + 1u) % constants::n_elements); + const auto edge = add_edge(vertex_id, target_id); auto row_view = sut.at(vertex_id); REQUIRE_EQ(std::ranges::count_if(row_view, &edge_type::is_valid), 1uz); @@ -502,7 +498,6 @@ TEST_CASE_TEMPLATE_DEFINE( "undirected adjacency matrix tests", SutType, undirected_adj_matrix_template ) { using fixture_type = test_undirected_adjacency_matrix; - using sut_type = typename fixture_type::sut_type; using edge_type = typename fixture_type::edge_type; fixture_type fixture; diff --git a/tests/source/gl/test_alg_bfs.cpp b/tests/source/gl/test_alg_bfs.cpp index 5b14ee7..0a55626 100644 --- a/tests/source/gl/test_alg_bfs.cpp +++ b/tests/source/gl/test_alg_bfs.cpp @@ -175,7 +175,6 @@ TEST_CASE_TEMPLATE_DEFINE( bfs_return_graph_template ) { using graph_type = GraphType; - using vertex_type = typename graph_type::vertex_type; const auto graph = gl::topology::regular_binary_tree(constants::depth); const auto pred_map = diff --git a/tests/source/gl/test_alg_dijkstra.cpp b/tests/source/gl/test_alg_dijkstra.cpp index 5e61032..3bc04d1 100644 --- a/tests/source/gl/test_alg_dijkstra.cpp +++ b/tests/source/gl/test_alg_dijkstra.cpp @@ -1,4 +1,5 @@ #include "gl/constants.hpp" +#include "gl/types/core.hpp" #include "testing/gl/alg_utils.hpp" #include "testing/gl/constants.hpp" #include "testing/gl/functional.hpp" @@ -65,10 +66,10 @@ TEST_CASE_TEMPLATE_DEFINE( source_id = 0uz; for (const auto id : sut.vertex_ids()) { - const auto parent_id = id == 0uz ? 0uz : (id - 1uz) / 2uz; + const auto parent_id = id == 0u ? 0u : (id - 1u) / 2u; expected_predecessors.push_back(parent_id); - const auto vertex_depth = static_cast(std::log2(id + 1uz)); + const auto vertex_depth = static_cast(std::floor(std::log2(id + 1uz))); expected_distances.push_back(vertex_depth); } } @@ -180,10 +181,10 @@ TEST_CASE_TEMPLATE_DEFINE( source_id = 0uz; for (const auto id : sut.vertex_ids()) { - const auto parent_id = id == 0uz ? 0uz : (id - 1uz) / 2uz; + const auto parent_id = id == 0u ? 0u : (id - 1u) / 2u; expected_predecessors.push_back(parent_id); - const auto vertex_depth = static_cast(std::log2(id + 1uz)); + const auto vertex_depth = static_cast(std::floor(std::log2(id + 1uz))); expected_distances.push_back(vertex_depth); } } @@ -218,7 +219,7 @@ TEST_CASE_TEMPLATE_INSTANTIATE( TEST_CASE("reconstruct_path should thow if the vertex is not reachable") { const std::vector predecessor_map = {0, 3, 1, gl::invalid_id}; - gl::default_id_type vertex_id = predecessor_map.size() - 1uz; + const auto vertex_id = static_cast(predecessor_map.size() - 1uz); CHECK_THROWS_AS( discard_result(gl::algorithm::reconstruct_path(predecessor_map, vertex_id)), diff --git a/tests/source/gl/test_alg_prim_mst.cpp b/tests/source/gl/test_alg_prim_mst.cpp index b4aac9a..4134150 100644 --- a/tests/source/gl/test_alg_prim_mst.cpp +++ b/tests/source/gl/test_alg_prim_mst.cpp @@ -46,7 +46,7 @@ TEST_CASE_TEMPLATE_DEFINE( } } - expected_weight = edge_weight * (sut.order() - 1uz); + expected_weight = edge_weight * static_cast(sut.order() - 1uz); } SUBCASE("custom graph") { @@ -131,7 +131,7 @@ TEST_CASE_TEMPLATE_DEFINE( for (const auto& edge : sut.adjacent_edges(vertex_id)) expected_edges.emplace_back(edge.source(), edge.target()); - const weight_type expected_weight = sut.order() - 1uz; + const auto expected_weight = static_cast(sut.order() - 1uz); const auto mst = gl::algorithm::edge_heap_prim_mst(sut, source_id); @@ -191,7 +191,7 @@ TEST_CASE_TEMPLATE_DEFINE( } } - expected_weight = edge_weight * (sut.order() - 1uz); + expected_weight = edge_weight * static_cast(sut.order() - 1uz); } SUBCASE("custom graph") { @@ -276,7 +276,7 @@ TEST_CASE_TEMPLATE_DEFINE( for (const auto& edge : sut.adjacent_edges(vertex_id)) expected_edges.emplace_back(edge.source(), edge.target()); - const weight_type expected_weight = sut.order() - 1uz; + const auto expected_weight = static_cast(sut.order() - 1uz); const auto mst = gl::algorithm::vertex_heap_prim_mst(sut, source_id); diff --git a/tests/source/gl/test_flat_jagged_vector.cpp b/tests/source/gl/test_flat_jagged_vector.cpp index 53a956a..c36df3c 100644 --- a/tests/source/gl/test_flat_jagged_vector.cpp +++ b/tests/source/gl/test_flat_jagged_vector.cpp @@ -1,6 +1,7 @@ -#include +#include "doctest.h" +#include "testing/common/wrnsup.hpp" -#include +#include #include #include @@ -91,7 +92,9 @@ TEST_CASE_FIXTURE( sut.push_back({1, 2, 3}); sut.push_back({4, 5}); + SUPPRESS_WARNING_BEGIN("-Wself-move"); sut = std::move(sut); + SUPPRESS_WARNING_END; CHECK_EQ(sut.size(), 2uz); CHECK_EQ(sut.data_size(), 5uz); diff --git a/tests/source/gl/test_flat_matrix.cpp b/tests/source/gl/test_flat_matrix.cpp index 1696d3d..912c44f 100644 --- a/tests/source/gl/test_flat_matrix.cpp +++ b/tests/source/gl/test_flat_matrix.cpp @@ -1,6 +1,7 @@ -#include +#include "doctest.h" +#include "testing/common/wrnsup.hpp" -#include +#include #include #include @@ -97,7 +98,9 @@ TEST_CASE_FIXTURE( sut.push_row({1, 2, 3}); sut.push_row({4, 5, 6}); + SUPPRESS_WARNING_BEGIN("-Wself-move"); sut = std::move(sut); + SUPPRESS_WARNING_END; CHECK_EQ(sut.size(), 2uz); CHECK_EQ(sut.n_rows(), 2uz); diff --git a/tests/source/gl/test_graph.cpp b/tests/source/gl/test_graph.cpp index e8f1dec..8182d78 100644 --- a/tests/source/gl/test_graph.cpp +++ b/tests/source/gl/test_graph.cpp @@ -1,5 +1,6 @@ #include "gl/directional_tags.hpp" #include "gl/graph_traits.hpp" +#include "gl/types/core.hpp" #include "testing/gl/constants.hpp" #include "testing/gl/functional.hpp" #include "testing/gl/types.hpp" @@ -10,6 +11,7 @@ #include #include +#include #include namespace gl_testing { @@ -87,7 +89,7 @@ struct test_graph { graph.vertex_ids(), [&graph, expected_n_edges = n_incident_edges_for_fully_connected_vertex(graph)]( const gl::default_id_type vertex_id - ) { return gl::util::range_size(graph.adjacent_edges(vertex_id)) == expected_n_edges; } + ) { return static_cast(gl::util::range_size(graph.adjacent_edges(vertex_id))) == expected_n_edges; } )); } @@ -214,7 +216,7 @@ TEST_CASE_TEMPLATE_DEFINE("graph structure tests", TraitsType, graph_traits_temp SUBCASE("get_vertex should throw if the given id is invalid") { sut_type sut{constants::n_elements}; - CHECK_THROWS_AS(static_cast(sut.get_vertex(sut.order())), std::out_of_range); + CHECK_THROWS_AS(static_cast(sut.get_vertex(static_cast(sut.order()))), std::out_of_range); } SUBCASE("get_vertex should return a vertex with the given id") { @@ -260,7 +262,7 @@ TEST_CASE_TEMPLATE_DEFINE("graph structure tests", TraitsType, graph_traits_temp REQUIRE(std::ranges::all_of( vertex_id_view, [&sut, expected_n_incident_edges](const gl::default_id_type vertex_id) { - return gl::util::range_size(sut.adjacent_edges(vertex_id)) + return static_cast(gl::util::range_size(sut.adjacent_edges(vertex_id))) == expected_n_incident_edges; } )); @@ -292,7 +294,7 @@ TEST_CASE_TEMPLATE_DEFINE("graph structure tests", TraitsType, graph_traits_temp REQUIRE(std::ranges::all_of( vertex_id_view, [&sut, expected_n_incident_edges](const gl::default_id_type vertex_id) { - return gl::util::range_size(sut.adjacent_edges(vertex_id)) + return static_cast(gl::util::range_size(sut.adjacent_edges(vertex_id))) == expected_n_incident_edges; } )); @@ -909,8 +911,8 @@ TEST_CASE_TEMPLATE_DEFINE("properties getter tests", TraitsType, property_graph_ sut_type sut{constants::n_elements}; for (auto vertex : sut.vertices()) { vertex.properties() = std::format("vertex_{}", vertex.id()); - sut.add_edge(vertex.id(), (vertex.id() + 1uz) % constants::n_elements).properties() = - std::format("edge_{}", vertex.id()); + const auto target_id = static_cast((vertex.id() + 1uz) % constants::n_elements); + sut.add_edge(vertex.id(), target_id).properties() = std::format("edge_{}", vertex.id()); } auto vmap = sut.vertex_properties_map(); @@ -930,7 +932,7 @@ TEST_CASE_TEMPLATE_DEFINE("properties getter tests", TraitsType, property_graph_ for (auto [id, property] : std::views::enumerate(emap)) { CHECK_EQ(property, std::format("edge_{}", id)); CHECK_EQ(emap[id], std::format("edge_{}", id)); - CHECK_EQ(sut.get_edge_properties(id), std::format("edge_{}", id)); + CHECK_EQ(sut.get_edge_properties(static_cast(id)), std::format("edge_{}", id)); } CHECK_THROWS_AS( diff --git a/tests/source/gl/test_graph_topology_builders.cpp b/tests/source/gl/test_graph_topology_builders.cpp index 6fcdf1f..498c527 100644 --- a/tests/source/gl/test_graph_topology_builders.cpp +++ b/tests/source/gl/test_graph_topology_builders.cpp @@ -77,9 +77,11 @@ template template [[nodiscard]] auto is_vertex_connected_to_next_only(const GraphType& graph) { + using id_type = typename GraphType::id_type; using vertex_type = typename GraphType::vertex_type; return [&graph](const vertex_type& source) { - const auto next_vertex = graph.get_vertex((source.id() + 1uz) % graph.order()); + const auto next_vertex_id = static_cast((source.id() + 1uz) % graph.order()); + const auto next_vertex = graph.get_vertex(next_vertex_id); return std::ranges::all_of(graph.vertices(), [&](const auto& vertex) { return (vertex == next_vertex) == graph.has_edge(source, vertex); @@ -89,10 +91,11 @@ template template [[nodiscard]] auto is_vertex_connected_to_prev_only(const GraphType& graph) { + using id_type = typename GraphType::id_type; using vertex_type = typename GraphType::vertex_type; return [&graph](const vertex_type& source) { - const auto prev_vertex = - graph.get_vertex((source.id() + graph.order() - 1uz) % graph.order()); + const auto prev_vertex_id = static_cast((source.id() + graph.order() - 1uz) % graph.order()); + const auto prev_vertex = graph.get_vertex(prev_vertex_id); return std::ranges::all_of(graph.vertices(), [&](const auto& vertex) { return (vertex == prev_vertex) == graph.has_edge(source, vertex); @@ -102,12 +105,14 @@ template template [[nodiscard]] auto is_vertex_connected_to_id_adjacent(const GraphType& graph) { + using id_type = typename GraphType::id_type; using vertex_type = typename GraphType::vertex_type; return [&graph](const vertex_type& source) { - const auto next_vertex = graph.get_vertex((source.id() + 1uz) % graph.order()); + const auto next_vertex_id = static_cast((source.id() + 1uz) % graph.order()); + const auto next_vertex = graph.get_vertex(next_vertex_id); - const auto prev_vertex = - graph.get_vertex((source.id() + graph.order() - 1uz) % graph.order()); + const auto prev_vertex_id = static_cast((source.id() + graph.order() - 1uz) % graph.order()); + const auto prev_vertex = graph.get_vertex(prev_vertex_id); return std::ranges::all_of(graph.vertices(), [&](const auto& vertex) { return (vertex == prev_vertex or vertex == next_vertex) @@ -139,7 +144,6 @@ template template [[nodiscard]] auto is_biconnected_to_binary_chlidren(const GraphType& graph) { - using vertex_type = typename GraphType::vertex_type; return [&graph](const gl::default_id_type source_id) { const auto target_ids = gl::topology::detail::get_binary_target_ids(source_id); const auto parent_id = source_id == 0u ? 0u : (source_id - 1u) / 2u; @@ -174,7 +178,6 @@ TEST_CASE_TEMPLATE_DEFINE( ) { using graph_type = GraphType; using vertex_type = typename graph_type::vertex_type; - using edge_type = typename graph_type::edge_type; SUBCASE("clique(n_vertices) should build a fully connected graph of size n_vertices") { const auto clique = gl::topology::clique(constants::n_elements_top); @@ -255,8 +258,6 @@ TEST_CASE_TEMPLATE_DEFINE( "directed graph specific topology builders tests", GraphType, directed_graph_type_template ) { using graph_type = GraphType; - using vertex_type = typename graph_type::vertex_type; - using edge_type = typename graph_type::edge_type; SUBCASE("cycle(n_vertices) should build a one-way cycle graph of size n_vertices") { const auto cycle = gl::topology::cycle(constants::n_elements_top); @@ -348,8 +349,6 @@ TEST_CASE_TEMPLATE_DEFINE( "undirected graph specific topology builders tests", GraphType, undirected_graph_type_template ) { using graph_type = GraphType; - using vertex_type = typename graph_type::vertex_type; - using edge_type = typename graph_type::edge_type; SUBCASE("cycle(n_vertices) should build a two-way cycle graph of size n_vertices") { graph_type cycle; diff --git a/tests/source/gl/test_vertex_degree_getters.cpp b/tests/source/gl/test_vertex_degree_getters.cpp index 8b2a44e..b6dadcc 100644 --- a/tests/source/gl/test_vertex_degree_getters.cpp +++ b/tests/source/gl/test_vertex_degree_getters.cpp @@ -17,7 +17,6 @@ TEST_CASE_TEMPLATE_DEFINE( "vertex degree getter tests for directed graphs", TraitsType, directed_graph_traits_template ) { using sut_type = gl::graph; - using vertex_type = typename sut_type::vertex_type; const auto n_vertices = constants::n_elements_top; @@ -105,7 +104,6 @@ TEST_CASE_TEMPLATE_DEFINE( "vertex degree getter tests for undirected graphs", TraitsType, undirected_graph_traits_template ) { using sut_type = gl::graph; - using vertex_type = typename sut_type::vertex_type; const auto n_vertices = constants::n_elements_top; diff --git a/tests/source/hgl/test_flat_incidence_list.cpp b/tests/source/hgl/test_flat_incidence_list.cpp index 8353915..01f2eb0 100644 --- a/tests/source/hgl/test_flat_incidence_list.cpp +++ b/tests/source/hgl/test_flat_incidence_list.cpp @@ -284,8 +284,8 @@ TEST_CASE_FIXTURE( REQUIRE(std::ranges::all_of(sut.degree_map(n_elements), is_zero)); REQUIRE(std::ranges::all_of(sut.hyperedge_size_map(n_elements), is_zero)); - for (std::size_t i = 0uz; i < n_elements; i++) - for (std::size_t j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind(i, j); const auto deg_map = sut.degree_map(n_elements); @@ -562,8 +562,8 @@ TEST_CASE_FIXTURE( REQUIRE(std::ranges::all_of(sut.degree_map(n_elements), is_zero)); REQUIRE(std::ranges::all_of(sut.hyperedge_size_map(n_elements), is_zero)); - for (std::size_t i = 0uz; i < n_elements; i++) - for (std::size_t j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind(i, j); const auto deg_map = sut.degree_map(n_elements); @@ -607,8 +607,8 @@ struct test_bf_directed_flat_incidence_list : public test_flat_incidence_list { ) { std::vector tail_bound, head_bound; - for (std::size_t i = 0uz; i < n_hyperedges; ++i) { - if (i % 2 == 0) { + for (auto i = 0u; i < n_hyperedges; ++i) { + if (i % 2u == 0u) { sut.bind_tail(vertex_id, i); tail_bound.push_back(i); } @@ -626,8 +626,8 @@ struct test_bf_directed_flat_incidence_list : public test_flat_incidence_list { ) { std::vector tail_bound, head_bound; - for (std::size_t i = 0uz; i < n_vertices; ++i) { - if (i % 2 == 0) { + for (auto i = 0u; i < n_vertices; ++i) { + if (i % 2u == 0u) { sut.bind_tail(i, hyperedge_id); tail_bound.push_back(i); } @@ -1023,8 +1023,8 @@ TEST_CASE_FIXTURE( REQUIRE(std::ranges::all_of(sut.head_size_map(n_elements), is_zero)); SUBCASE("tail bind") { - for (std::size_t i = 0uz; i < n_elements; i++) - for (std::size_t j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind_tail(i, j); const auto deg_map = sut.degree_map(n_elements); @@ -1042,8 +1042,8 @@ TEST_CASE_FIXTURE( } SUBCASE("head bind") { - for (std::size_t i = 0uz; i < n_elements; i++) - for (std::size_t j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind_head(i, j); const auto deg_map = sut.degree_map(n_elements); @@ -1061,8 +1061,8 @@ TEST_CASE_FIXTURE( } // diagonal = tail, everything else is head - for (std::size_t i = 0uz; i < n_elements; i++) { - for (std::size_t j = 0uz; j <= i; j++) { + for (auto i = 0u; i < n_elements; i++) { + for (auto j = 0u; j <= i; j++) { if (i == j) sut.bind_tail(i, j); else @@ -1506,8 +1506,8 @@ TEST_CASE_FIXTURE( REQUIRE(std::ranges::all_of(sut.head_size_map(n_elements), is_zero)); SUBCASE("tail bind") { - for (std::size_t i = 0uz; i < n_elements; i++) - for (std::size_t j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind_tail(i, j); const auto deg_map = sut.degree_map(n_elements); @@ -1525,8 +1525,8 @@ TEST_CASE_FIXTURE( } SUBCASE("head bind") { - for (std::size_t i = 0uz; i < n_elements; i++) - for (std::size_t j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind_head(i, j); const auto deg_map = sut.degree_map(n_elements); @@ -1544,8 +1544,8 @@ TEST_CASE_FIXTURE( } // diagonal = tail, everything else is head - for (std::size_t i = 0uz; i < n_elements; i++) { - for (std::size_t j = 0uz; j <= i; j++) { + for (auto i = 0u; i < n_elements; i++) { + for (auto j = 0u; j <= i; j++) { if (i == j) sut.bind_tail(i, j); else From 403f2a810d7549d1d90aa968340bb0ddb8d73456 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Fri, 3 Apr 2026 22:59:02 +0200 Subject: [PATCH 2/7] some more fixes --- include/gl/util/ranges.hpp | 150 ++++++++++++++++++++- include/hgl/impl/flat_incidence_list.hpp | 30 ++--- include/hgl/impl/incidence_list.hpp | 23 ++-- include/hgl/util.hpp | 2 + tests/source/hgl/test_hypergraph.cpp | 28 ++-- tests/source/hgl/test_incidence_list.cpp | 40 +++--- tests/source/hgl/test_incidence_matrix.cpp | 40 +++--- 7 files changed, 228 insertions(+), 85 deletions(-) diff --git a/include/gl/util/ranges.hpp b/include/gl/util/ranges.hpp index 5286d71..7414457 100644 --- a/include/gl/util/ranges.hpp +++ b/include/gl/util/ranges.hpp @@ -9,9 +9,6 @@ namespace gl::util { -inline constexpr auto deref_view = - std::views::transform([](auto&& p) -> decltype(auto) { return *p; }); - template constexpr auto range_size(R&& r) { if constexpr (std::ranges::sized_range) @@ -39,4 +36,151 @@ template return std::ranges::all_of(range, [&k](const auto& val) { return val == k; }); } +inline constexpr auto deref_view = + std::views::transform([](auto&& p) -> decltype(auto) { return *p; }); + +// TODO: add tests + +/// @brief A generic view that concatenates two ranges/views. +/// @todo Replace with `std::views::concat` (Requires C++26) +template +class concat_view : public std::ranges::view_interface> { +public: + concat_view() = default; + + constexpr concat_view(V1 v1, V2 v2) : _v1(std::move(v1)), _v2(std::move(v2)) {} + + constexpr auto begin() { + return iterator( + std::ranges::begin(this->_v1), + std::ranges::end(this->_v1), + std::ranges::begin(this->_v2) + ); + } + + constexpr auto begin() const + requires std::ranges::range && std::ranges::range + { + return iterator( + std::ranges::begin(this->_v1), + std::ranges::end(this->_v1), + std::ranges::begin(this->_v2) + ); + } + + constexpr auto end() { + return sentinel(std::ranges::end(this->_v2)); + } + + constexpr auto end() const + requires std::ranges::range && std::ranges::range + { + return sentinel(std::ranges::end(this->_v2)); + } + +private: + V1 _v1; + V2 _v2; + + template + class sentinel; // forward declare + + template + class iterator { + private: + using BaseV1 = std::conditional_t; + using BaseV2 = std::conditional_t; + + public: + using difference_type = std::common_type_t< + std::ranges::range_difference_t, + std::ranges::range_difference_t>; + using value_type = std:: + common_type_t, std::ranges::range_value_t>; + using reference = std::common_reference_t< + std::ranges::range_reference_t, + std::ranges::range_reference_t>; + using iterator_category = std::forward_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + iterator() = default; + + constexpr iterator( + std::ranges::iterator_t it1, + std::ranges::sentinel_t end1, + std::ranges::iterator_t it2 + ) + : _it1(std::move(it1)), _end1(std::move(end1)), _it2(std::move(it2)) {} + + constexpr reference operator*() const { + return (this->_it1 != this->_end1) ? *this->_it1 : *this->_it2; + } + + constexpr iterator& operator++() { + (this->_it1 != this->_end1) ? ++this->_it1 : ++this->_it2; + return *this; + } + + constexpr iterator operator++(int) { + iterator tmp = *this; + ++*this; + return tmp; + } + + constexpr bool operator==(const iterator& other) const { + return this->_it1 == other._it1 && this->_it2 == other._it2; + } + + constexpr bool operator==(const sentinel& s) const { + return this->_it1 == this->_end1 && this->_it2 == s.end2(); + } + + private: + std::ranges::iterator_t _it1{}; + std::ranges::sentinel_t _end1{}; + std::ranges::iterator_t _it2{}; + + friend class concat_view; + }; + + template + class sentinel { + private: + using BaseV2 = std::conditional_t; + + public: + sentinel() = default; + + constexpr explicit sentinel(std::ranges::sentinel_t end2) + : _end2(std::move(end2)) {} + + constexpr auto end2() const { + return this->_end2; + } + + private: + std::ranges::sentinel_t _end2{}; + + friend class concat_view; + friend class iterator; + }; +}; + +namespace detail { + +struct concat_fn { + template + constexpr auto operator()(R1&& r1, R2&& r2) const { + return concat_view, std::views::all_t>( + std::views::all(std::forward(r1)), std::views::all(std::forward(r2)) + ); + } +}; + +} // namespace detail + +/// @brief Concatenates two ranges sequentially into a single view. +/// @todo replace with `std::views::concat` +inline constexpr detail::concat_fn concat{}; + } // namespace gl::util diff --git a/include/hgl/impl/flat_incidence_list.hpp b/include/hgl/impl/flat_incidence_list.hpp index ca2eec0..3260d36 100644 --- a/include/hgl/impl/flat_incidence_list.hpp +++ b/include/hgl/impl/flat_incidence_list.hpp @@ -4,12 +4,12 @@ #pragma once -#include "gl/types/core.hpp" #include "hgl/constants.hpp" #include "hgl/decl/impl_tags.hpp" #include "hgl/directional_tags.hpp" #include "hgl/impl/layout_tags.hpp" #include "hgl/types.hpp" +#include "hgl/util.hpp" #include #include @@ -496,15 +496,10 @@ class flat_incidence_list final { ) const noexcept { const auto idx = to_idx(id); if constexpr (std::same_as) { - // TODO: use std::views::concat (C++26) - // NOTE: This is safe because the range operator | creates an owning view over the array - return std::array{ - this->_tail_storage[idx], this->_head_storage[idx] - } - | std::views::join; + return util::concat(this->_tail_storage[idx], this->_head_storage[idx]); } else { - return std::invoke(storage_proj, this)[idx]; + return (this->*storage_proj)[idx]; } } @@ -545,10 +540,7 @@ class flat_incidence_list final { [[nodiscard]] gl_attr_force_inline auto _get(const id_type id) const noexcept { if constexpr (Element == layout_tag::major_element) { // get major const auto idx = to_idx(id); - return std::array{ - this->_tail_storage[idx], this->_head_storage[idx] - } - | std::views::join; + return util::concat(this->_tail_storage[idx], this->_head_storage[idx]); } else { // get minor return std::views::iota(initial_id_v, this->_tail_storage.size()) @@ -563,14 +555,14 @@ class flat_incidence_list final { [[nodiscard]] gl_attr_force_inline auto _get(const id_type id, const auto&& storage_proj) const noexcept { if constexpr (Element == layout_tag::major_element) { // get major - return std::invoke(storage_proj, this)[to_idx(id)]; + return (this->*storage_proj)[to_idx(id)]; } else { // get minor return std::views::iota(initial_id_v, this->_tail_storage.size()) | std::views::filter( - [this, &storage = std::invoke(storage_proj, this), minor_id = id]( - id_type major_id - ) { return detail::contains(storage[to_idx(major_id)], minor_id); } + [this, &storage = this->*storage_proj, minor_id = id](id_type major_id) { + return detail::contains(storage[to_idx(major_id)], minor_id); + } ); } } @@ -597,11 +589,11 @@ class flat_incidence_list final { [[nodiscard]] gl_attr_force_inline size_type _size(const id_type id, const auto&& storage_proj) const noexcept { if constexpr (Element == layout_tag::major_element) { // size major - return std::invoke(storage_proj, this)[to_idx(id)].size(); + return (this->*storage_proj)[to_idx(id)].size(); } else { // size minor size_type size = 0uz; - for (const auto segment : std::invoke(storage_proj, this)) + for (const auto segment : this->*storage_proj) if (detail::contains(segment, id)) ++size; return size; @@ -636,7 +628,7 @@ class flat_incidence_list final { [[nodiscard]] std::vector _size_map( const size_type n_elements, const auto&& storage_proj ) const noexcept { - const auto& storage = std::invoke(storage_proj, this); + const auto& storage = this->*storage_proj; if constexpr (Element == layout_tag::major_element) { // size major std::vector size_map(n_elements, 0uz); diff --git a/include/hgl/impl/incidence_list.hpp b/include/hgl/impl/incidence_list.hpp index 6dc8373..3590782 100644 --- a/include/hgl/impl/incidence_list.hpp +++ b/include/hgl/impl/incidence_list.hpp @@ -4,12 +4,12 @@ #pragma once -#include "gl/types/core.hpp" #include "hgl/constants.hpp" #include "hgl/decl/impl_tags.hpp" #include "hgl/directional_tags.hpp" #include "hgl/impl/layout_tags.hpp" #include "hgl/types.hpp" +#include "hgl/util.hpp" #include #include @@ -481,10 +481,7 @@ class incidence_list final { [[nodiscard]] gl_attr_force_inline auto _get(const id_type id) const noexcept { if constexpr (Element == layout_tag::major_element) { // get major const auto idx = to_idx(id); - return std::array, 2>{ - this->_tail_storage[idx], this->_head_storage[idx] - } - | std::views::join; + return util::concat(this->_tail_storage[idx], this->_head_storage[idx]); } else { // get minor return std::views::iota(initial_id_v, this->_tail_storage.size()) @@ -499,14 +496,14 @@ class incidence_list final { [[nodiscard]] gl_attr_force_inline auto _get(const id_type id, const Projection storage_proj) const noexcept { if constexpr (Element == layout_tag::major_element) { // get major - return std::views::all(std::invoke(storage_proj, this)[to_idx(id)]); + return std::views::all((this->*storage_proj)[to_idx(id)]); } else { // get minor return std::views::iota(initial_id_v, this->_tail_storage.size()) | std::views::filter( - [this, &storage = std::invoke(storage_proj, this), minor_id = id]( - id_type major_id - ) { return this->_contains(storage[to_idx(major_id)], minor_id); } + [this, &storage = this->*storage_proj, minor_id = id](id_type major_id) { + return this->_contains(storage[to_idx(major_id)], minor_id); + } ); } } @@ -533,11 +530,11 @@ class incidence_list final { [[nodiscard]] gl_attr_force_inline size_type _size(const id_type id, const Projection storage_proj) const noexcept { if constexpr (Element == layout_tag::major_element) { // size major - return std::invoke(storage_proj, this)[to_idx(id)].size(); + return (this->*storage_proj)[to_idx(id)].size(); } else { // size minor size_type size = 0uz; - for (const auto& minor_storage : std::invoke(storage_proj, this)) + for (const auto& minor_storage : this->*storage_proj) if (this->_contains(minor_storage, id)) ++size; return size; @@ -571,7 +568,7 @@ class incidence_list final { ) const noexcept { if constexpr (Element == layout_tag::major_element) { // size major std::vector size_map(n_elements, 0uz); - const auto& storage = std::invoke(storage_proj, this); + const auto& storage = this->*storage_proj; const auto n_segments = storage.size(); for (auto i = 0uz; i < n_segments; ++i) size_map[i] = storage[i].size(); @@ -579,7 +576,7 @@ class incidence_list final { } else { // size minor std::vector size_map(n_elements, 0uz); - for (const auto& minor_storage : std::invoke(storage_proj, this)) + for (const auto& minor_storage : this->*storage_proj) for (const auto minor_id : minor_storage) ++size_map[to_idx(minor_id)]; return size_map; diff --git a/include/hgl/util.hpp b/include/hgl/util.hpp index d9b3b21..4eb89d2 100644 --- a/include/hgl/util.hpp +++ b/include/hgl/util.hpp @@ -9,6 +9,8 @@ namespace hgl::util { using gl::util::all_equal; +using gl::util::concat; +using gl::util::concat_view; using gl::util::deref_view; using gl::util::is_constant; using gl::util::range_size; diff --git a/tests/source/hgl/test_hypergraph.cpp b/tests/source/hgl/test_hypergraph.cpp index d9bc895..ddce5f8 100644 --- a/tests/source/hgl/test_hypergraph.cpp +++ b/tests/source/hgl/test_hypergraph.cpp @@ -1,12 +1,12 @@ -#include "gl/types/properties.hpp" -#include "hgl/directional_tags.hpp" -#include "hgl/hypergraph.hpp" -#include "hgl/hypergraph_traits.hpp" -#include "hgl/impl/layout_tags.hpp" +#include "doctest.h" +#include "testing/common/wrnsup.hpp" #include "testing/hgl/constants.hpp" #include "testing/hgl/types.hpp" -#include +#include +#include +#include +#include #include #include @@ -524,6 +524,8 @@ TEST_CASE_TEMPLATE_DEFINE( } SUBCASE("incident_hyperedges and degree should throw if the given vertex (id) is invalid") { + SUPPRESS_WARNING_BEGIN("-Warray-bounds"); + sut_type sut{constants::n_vertices, constants::n_hyperedges}; CHECK_THROWS_AS( static_cast(sut.incident_hyperedges(vertex_type{constants::out_of_rng_vid})), @@ -532,6 +534,8 @@ TEST_CASE_TEMPLATE_DEFINE( CHECK_THROWS_AS( static_cast(sut.degree(vertex_type{constants::out_of_rng_vid})), std::out_of_range ); + + SUPPRESS_WARNING_END; } SUBCASE("incident_hyperedges should return an empty view by default and degree should return 0 " @@ -652,6 +656,8 @@ TEST_CASE_TEMPLATE_DEFINE( SUBCASE("incident_vertices and hyperedge_size should throw if the given hyperedge (id) is " "invalid") { + SUPPRESS_WARNING_BEGIN("-Warray-bounds"); + sut_type sut{constants::n_vertices, constants::n_hyperedges}; CHECK_THROWS_AS( static_cast(sut.incident_vertices(hyperedge_type{constants::out_of_rng_eid})), @@ -661,6 +667,8 @@ TEST_CASE_TEMPLATE_DEFINE( static_cast(sut.hyperedge_size(hyperedge_type{constants::out_of_rng_eid})), std::out_of_range ); + + SUPPRESS_WARNING_END; } SUBCASE("incident_vertices should return an empty view by default and hyperedge_size should " @@ -791,8 +799,8 @@ TEST_CASE_TEMPLATE_DEFINE( REQUIRE(std::ranges::all_of(sut.hyperedge_size_map(), is_zero)); if constexpr (std::same_as) { - for (auto i = 0uz; i < n_elements; i++) - for (auto j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind(i, j); const auto deg_map = sut.degree_map(); @@ -810,8 +818,8 @@ TEST_CASE_TEMPLATE_DEFINE( REQUIRE(std::ranges::all_of(sut.tail_size_map(), is_zero)); REQUIRE(std::ranges::all_of(sut.head_size_map(), is_zero)); - for (auto i = 0uz; i < n_elements; i++) { - for (auto j = 0uz; j <= i; j++) { + for (auto i = 0u; i < n_elements; i++) { + for (auto j = 0u; j <= i; j++) { if (i == j) sut.bind_tail(i, j); else diff --git a/tests/source/hgl/test_incidence_list.cpp b/tests/source/hgl/test_incidence_list.cpp index b55ddcb..3332aa4 100644 --- a/tests/source/hgl/test_incidence_list.cpp +++ b/tests/source/hgl/test_incidence_list.cpp @@ -276,8 +276,8 @@ TEST_CASE_FIXTURE( REQUIRE(std::ranges::all_of(sut.degree_map(n_elements), is_zero)); REQUIRE(std::ranges::all_of(sut.hyperedge_size_map(n_elements), is_zero)); - for (auto i = 0uz; i < n_elements; i++) - for (auto j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind(i, j); const auto deg_map = sut.degree_map(n_elements); @@ -551,8 +551,8 @@ TEST_CASE_FIXTURE( REQUIRE(std::ranges::all_of(sut.degree_map(n_elements), is_zero)); REQUIRE(std::ranges::all_of(sut.hyperedge_size_map(n_elements), is_zero)); - for (auto i = 0uz; i < n_elements; i++) - for (auto j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind(i, j); const auto deg_map = sut.degree_map(n_elements); @@ -596,8 +596,8 @@ struct test_bf_directed_incidence_list : public test_incidence_list { ) { std::vector tail_bound, head_bound; - for (auto i = 0uz; i < n_hyperedges; ++i) { - if (i % 2 == 0) { + for (auto i = 0u; i < n_hyperedges; ++i) { + if (i % 2u == 0u) { sut.bind_tail(vertex_id, i); tail_bound.push_back(i); } @@ -615,8 +615,8 @@ struct test_bf_directed_incidence_list : public test_incidence_list { ) { std::vector tail_bound, head_bound; - for (auto i = 0uz; i < n_vertices; ++i) { - if (i % 2 == 0) { + for (auto i = 0u; i < n_vertices; ++i) { + if (i % 2u == 0u) { sut.bind_tail(i, hyperedge_id); tail_bound.push_back(i); } @@ -1008,8 +1008,8 @@ TEST_CASE_FIXTURE( REQUIRE(std::ranges::all_of(sut.head_size_map(n_elements), is_zero)); SUBCASE("tail bind") { - for (auto i = 0uz; i < n_elements; i++) - for (auto j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind_tail(i, j); const auto deg_map = sut.degree_map(n_elements); @@ -1027,8 +1027,8 @@ TEST_CASE_FIXTURE( } SUBCASE("head bind") { - for (auto i = 0uz; i < n_elements; i++) - for (auto j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind_head(i, j); const auto deg_map = sut.degree_map(n_elements); @@ -1046,8 +1046,8 @@ TEST_CASE_FIXTURE( } // diagonal = tail, everything else is head - for (auto i = 0uz; i < n_elements; i++) { - for (auto j = 0uz; j <= i; j++) { + for (auto i = 0u; i < n_elements; i++) { + for (auto j = 0u; j <= i; j++) { if (i == j) sut.bind_tail(i, j); else @@ -1488,8 +1488,8 @@ TEST_CASE_FIXTURE( REQUIRE(std::ranges::all_of(sut.head_size_map(n_elements), is_zero)); SUBCASE("tail bind") { - for (auto i = 0uz; i < n_elements; i++) - for (auto j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind_tail(i, j); const auto deg_map = sut.degree_map(n_elements); @@ -1507,8 +1507,8 @@ TEST_CASE_FIXTURE( } SUBCASE("head bind") { - for (auto i = 0uz; i < n_elements; i++) - for (auto j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind_head(i, j); const auto deg_map = sut.degree_map(n_elements); @@ -1526,8 +1526,8 @@ TEST_CASE_FIXTURE( } // diagonal = tail, everything else is head - for (auto i = 0uz; i < n_elements; i++) { - for (auto j = 0uz; j <= i; j++) { + for (auto i = 0u; i < n_elements; i++) { + for (auto j = 0u; j <= i; j++) { if (i == j) sut.bind_tail(i, j); else diff --git a/tests/source/hgl/test_incidence_matrix.cpp b/tests/source/hgl/test_incidence_matrix.cpp index e091a2d..45dd558 100644 --- a/tests/source/hgl/test_incidence_matrix.cpp +++ b/tests/source/hgl/test_incidence_matrix.cpp @@ -320,8 +320,8 @@ TEST_CASE_FIXTURE( REQUIRE(std::ranges::all_of(sut.degree_map(n_elements), is_zero)); REQUIRE(std::ranges::all_of(sut.hyperedge_size_map(n_elements), is_zero)); - for (auto i = 0uz; i < n_elements; i++) - for (auto j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind(i, j); const auto deg_map = sut.degree_map(n_elements); @@ -648,8 +648,8 @@ TEST_CASE_FIXTURE( REQUIRE(std::ranges::all_of(sut.degree_map(n_elements), is_zero)); REQUIRE(std::ranges::all_of(sut.hyperedge_size_map(n_elements), is_zero)); - for (auto i = 0uz; i < n_elements; i++) - for (auto j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind(i, j); const auto deg_map = sut.degree_map(n_elements); @@ -699,8 +699,8 @@ struct test_bf_directed_incidence_matrix : public test_incidence_matrix { ) { std::vector tail_bound, head_bound; - for (auto i = 0uz; i < n_hyperedges; ++i) { - if (i % 2 == 0) { + for (auto i = 0u; i < n_hyperedges; ++i) { + if (i % 2u == 0u) { sut.bind_tail(vertex_id, i); tail_bound.push_back(i); } @@ -718,8 +718,8 @@ struct test_bf_directed_incidence_matrix : public test_incidence_matrix { ) { std::vector tail_bound, head_bound; - for (auto i = 0uz; i < n_vertices; ++i) { - if (i % 2 == 0) { + for (auto i = 0u; i < n_vertices; ++i) { + if (i % 2u == 0u) { sut.bind_tail(i, hyperedge_id); tail_bound.push_back(i); } @@ -1085,8 +1085,8 @@ TEST_CASE_FIXTURE( REQUIRE(std::ranges::all_of(sut.head_size_map(n_elements), is_zero)); SUBCASE("tail bind") { - for (auto i = 0uz; i < n_elements; i++) - for (auto j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind_tail(i, j); const auto deg_map = sut.degree_map(n_elements); @@ -1104,8 +1104,8 @@ TEST_CASE_FIXTURE( } SUBCASE("head bind") { - for (auto i = 0uz; i < n_elements; i++) - for (auto j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind_head(i, j); const auto deg_map = sut.degree_map(n_elements); @@ -1123,8 +1123,8 @@ TEST_CASE_FIXTURE( } // diagonal = tail, everything else is head - for (auto i = 0uz; i < n_elements; i++) { - for (auto j = 0uz; j <= i; j++) { + for (auto i = 0u; i < n_elements; i++) { + for (auto j = 0u; j <= i; j++) { if (i == j) sut.bind_tail(i, j); else @@ -1539,8 +1539,8 @@ TEST_CASE_FIXTURE( REQUIRE(std::ranges::all_of(sut.head_size_map(n_elements), is_zero)); SUBCASE("tail bind") { - for (auto i = 0uz; i < n_elements; i++) - for (auto j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind_tail(i, j); const auto deg_map = sut.degree_map(n_elements); @@ -1558,8 +1558,8 @@ TEST_CASE_FIXTURE( } SUBCASE("head bind") { - for (auto i = 0uz; i < n_elements; i++) - for (auto j = 0uz; j <= i; j++) + for (auto i = 0u; i < n_elements; i++) + for (auto j = 0u; j <= i; j++) sut.bind_head(i, j); const auto deg_map = sut.degree_map(n_elements); @@ -1577,8 +1577,8 @@ TEST_CASE_FIXTURE( } // diagonal = tail, everything else is head - for (auto i = 0uz; i < n_elements; i++) { - for (auto j = 0uz; j <= i; j++) { + for (auto i = 0u; i < n_elements; i++) { + for (auto j = 0u; j <= i; j++) { if (i == j) sut.bind_tail(i, j); else From ac69a7ec86f5ef7641e4ee0c9203e89f1ebfcf68 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Fri, 3 Apr 2026 23:18:38 +0200 Subject: [PATCH 3/7] wip: alignment --- include/gl/topology/binary_tree.hpp | 2 +- include/gl/util/{pow.hpp => math.hpp} | 0 include/gl/util/ranges.hpp | 18 ++++++++++++++---- tests/source/gl/test_util.cpp | 8 ++++++-- tests/source/hgl/test_flat_incidence_list.cpp | 7 ++++--- tests/source/hgl/test_incidence_list.cpp | 7 ++++--- 6 files changed, 29 insertions(+), 13 deletions(-) rename include/gl/util/{pow.hpp => math.hpp} (100%) diff --git a/include/gl/topology/binary_tree.hpp b/include/gl/topology/binary_tree.hpp index bd01f56..dd93d69 100644 --- a/include/gl/topology/binary_tree.hpp +++ b/include/gl/topology/binary_tree.hpp @@ -7,7 +7,7 @@ #include "gl/constants.hpp" #include "gl/conversion.hpp" #include "gl/graph.hpp" -#include "gl/util/pow.hpp" +#include "gl/util/math.hpp" #include diff --git a/include/gl/util/pow.hpp b/include/gl/util/math.hpp similarity index 100% rename from include/gl/util/pow.hpp rename to include/gl/util/math.hpp diff --git a/include/gl/util/ranges.hpp b/include/gl/util/ranges.hpp index 7414457..6f279c5 100644 --- a/include/gl/util/ranges.hpp +++ b/include/gl/util/ranges.hpp @@ -41,8 +41,16 @@ inline constexpr auto deref_view = // TODO: add tests -/// @brief A generic view that concatenates two ranges/views. -/// @todo Replace with `std::views::concat` (Requires C++26) +/// @brief A view concatenating two ranges sequentially (C++20 polyfill for C++26 `std::views::concat`). +/// +/// @warning **GCC 13/14 Bug:** Using branching views (like this or `std::ranges::filter_view`) +/// inside complex algorithms (e.g., `std::ranges::is_permutation`) may trigger false-positive +/// `-Wmaybe-uninitialized` warnings. To work around this, suppress the warning at the call site +/// or materialize the view into a contiguous container like `std::vector`. +/// +/// @tparam V1 First view type. +/// @tparam V2 Second view type. +/// @todo Replace with `std::views::concat` (C++26). template class concat_view : public std::ranges::view_interface> { public: @@ -179,8 +187,10 @@ struct concat_fn { } // namespace detail -/// @brief Concatenates two ranges sequentially into a single view. -/// @todo replace with `std::views::concat` +/// @brief Concatenates two viewable ranges into a `concat_view`. +/// @param r1 First range to concatenate. +/// @param r2 Second range to concatenate. +/// @todo Replace with `std::views::concat` (C++26). inline constexpr detail::concat_fn concat{}; } // namespace gl::util diff --git a/tests/source/gl/test_util.cpp b/tests/source/gl/test_util.cpp index a92c886..1777a73 100644 --- a/tests/source/gl/test_util.cpp +++ b/tests/source/gl/test_util.cpp @@ -1,6 +1,10 @@ -#include +#include "doctest.h" -#include +#include +#include + +#include +#include namespace gl_testing { diff --git a/tests/source/hgl/test_flat_incidence_list.cpp b/tests/source/hgl/test_flat_incidence_list.cpp index 01f2eb0..cffb8d9 100644 --- a/tests/source/hgl/test_flat_incidence_list.cpp +++ b/tests/source/hgl/test_flat_incidence_list.cpp @@ -1231,9 +1231,10 @@ TEST_CASE_FIXTURE( const auto [tail_bound_hyperedges, head_bound_hyperedges] = altbind_to_vertex(sut, vertex_id, constants::n_hyperedges); - CHECK(std::ranges::is_permutation( - sut.incident_hyperedges(vertex_id), constants::hyperedge_ids_view - )); + // NOTE: An explicit allocation is needed because of the GCC's false positive -Wmaybe-uninitialized warning + const auto incident_hyperedges = + sut.incident_hyperedges(vertex_id) | std::ranges::to(); + CHECK(std::ranges::is_permutation(incident_hyperedges, constants::hyperedge_ids_view)); CHECK(std::ranges::equal(sut.out_hyperedges(vertex_id), tail_bound_hyperedges)); CHECK(std::ranges::equal(sut.in_hyperedges(vertex_id), head_bound_hyperedges)); } diff --git a/tests/source/hgl/test_incidence_list.cpp b/tests/source/hgl/test_incidence_list.cpp index 3332aa4..0c2a3b6 100644 --- a/tests/source/hgl/test_incidence_list.cpp +++ b/tests/source/hgl/test_incidence_list.cpp @@ -1214,9 +1214,10 @@ TEST_CASE_FIXTURE( const auto [tail_bound_hyperedges, head_bound_hyperedges] = altbind_to_vertex(sut, vertex_id, constants::n_hyperedges); - CHECK(std::ranges::is_permutation( - sut.incident_hyperedges(vertex_id), constants::hyperedge_ids_view - )); + // NOTE: An explicit allocation is needed because of the GCC's false positive -Wmaybe-uninitialized warning + const auto incident_hyperedges = + sut.incident_hyperedges(vertex_id) | std::ranges::to(); + CHECK(std::ranges::is_permutation(incident_hyperedges, constants::hyperedge_ids_view)); CHECK(std::ranges::equal(sut.out_hyperedges(vertex_id), tail_bound_hyperedges)); CHECK(std::ranges::equal(sut.in_hyperedges(vertex_id), head_bound_hyperedges)); } From b090f69b7fdd41af741f6071ea607638851b16fc Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Fri, 3 Apr 2026 23:40:03 +0200 Subject: [PATCH 4/7] concat_view tests --- include/gl/graph.hpp | 1 + include/gl/util/ranges.hpp | 5 +- include/hgl/conversion.hpp | 6 +- include/hgl/impl/incidence_matrix.hpp | 8 +- tests/include/testing/common/wrnsup.hpp | 4 +- tests/source/gl/test_adjacency_matrix.cpp | 3 +- tests/source/gl/test_alg_dijkstra.cpp | 6 +- tests/source/gl/test_graph.cpp | 19 ++++- .../gl/test_graph_topology_builders.cpp | 6 +- tests/source/gl/test_util.cpp | 78 +++++++++++++++++++ 10 files changed, 119 insertions(+), 17 deletions(-) diff --git a/include/gl/graph.hpp b/include/gl/graph.hpp index 15349ed..7ac6c18 100644 --- a/include/gl/graph.hpp +++ b/include/gl/graph.hpp @@ -9,6 +9,7 @@ #include "gl/impl/impl_tags.hpp" #include "gl/io/stream_options_manipulator.hpp" #include "gl/util/ranges.hpp" + #include #include diff --git a/include/gl/util/ranges.hpp b/include/gl/util/ranges.hpp index 6f279c5..9b7d281 100644 --- a/include/gl/util/ranges.hpp +++ b/include/gl/util/ranges.hpp @@ -125,7 +125,10 @@ class concat_view : public std::ranges::view_interface> { } constexpr iterator& operator++() { - (this->_it1 != this->_end1) ? ++this->_it1 : ++this->_it2; + if (this->_it1 != this->_end1) + ++this->_it1; + else + ++this->_it2; return *this; } diff --git a/include/hgl/conversion.hpp b/include/hgl/conversion.hpp index c8b2b21..8246d0f 100644 --- a/include/hgl/conversion.hpp +++ b/include/hgl/conversion.hpp @@ -297,7 +297,8 @@ template using g_id_type = typename G::id_type; G g{h.order() + h.size()}; - const auto align_edge_id = [shift = static_cast(h.order())](const auto eid) -> g_id_type { + const auto align_edge_id = + [shift = static_cast(h.order())](const auto eid) -> g_id_type { return eid + shift; }; @@ -323,7 +324,8 @@ template using g_id_type = typename G::id_type; G g{h.order() + h.size()}; - const auto align_edge_id = [shift = static_cast(h.order())](const auto eid) -> g_id_type { + const auto align_edge_id = + [shift = static_cast(h.order())](const auto eid) -> g_id_type { return eid + shift; }; diff --git a/include/hgl/impl/incidence_matrix.hpp b/include/hgl/impl/incidence_matrix.hpp index e13e717..9913c48 100644 --- a/include/hgl/impl/incidence_matrix.hpp +++ b/include/hgl/impl/incidence_matrix.hpp @@ -183,7 +183,9 @@ class incidence_matrix final { gl_attr_force_inline auto _incident_with(const id_type id) const noexcept { if constexpr (Element == layout_tag::major_element) { // incident with major return std::views::iota(initial_id_v, this->_matrix_row_size) - | std::views::filter([&row = this->_matrix[to_idx(id)]](id_type minor_id) { return row[to_idx(minor_id)]; }); + | std::views::filter([&row = this->_matrix[to_idx(id)]](id_type minor_id) { + return row[to_idx(minor_id)]; + }); } else { // incident with minor return std::views::iota(initial_id_v, this->_matrix.size()) @@ -487,8 +489,8 @@ class incidence_matrix final { else { // query minor return std::views::iota(initial_id_v, this->_matrix.size()) | std::views::filter([this, minor_idx = to_idx(id), pred](id_type major_id) { - return pred(this->_matrix[to_idx(major_id)][minor_idx]); - }); + return pred(this->_matrix[to_idx(major_id)][minor_idx]); + }); } } diff --git a/tests/include/testing/common/wrnsup.hpp b/tests/include/testing/common/wrnsup.hpp index b48ac5e..fb1a406 100644 --- a/tests/include/testing/common/wrnsup.hpp +++ b/tests/include/testing/common/wrnsup.hpp @@ -4,8 +4,8 @@ #define SUPPRESS_PRAGMA(x) _Pragma(#x) -#define SUPPRESS_WARNING_BEGIN(w) \ - SUPPRESS_PRAGMA(GCC diagnostic push) \ +#define SUPPRESS_WARNING_BEGIN(w) \ + SUPPRESS_PRAGMA(GCC diagnostic push) \ SUPPRESS_PRAGMA(GCC diagnostic ignored w) #define SUPPRESS_WARNING_END SUPPRESS_PRAGMA(GCC diagnostic pop) diff --git a/tests/source/gl/test_adjacency_matrix.cpp b/tests/source/gl/test_adjacency_matrix.cpp index acb0422..1379e56 100644 --- a/tests/source/gl/test_adjacency_matrix.cpp +++ b/tests/source/gl/test_adjacency_matrix.cpp @@ -225,7 +225,8 @@ TEST_CASE_TEMPLATE_DEFINE("directed adjacency matrix tests", SutType, directed_a SUBCASE("at should return a view equivalent to the matrix row of the given vertex") { for (const auto vertex_id : std::views::iota(constants::v1_id, constants::n_elements)) { - const auto target_id = static_cast((vertex_id + 1u) % constants::n_elements); + const auto target_id = + static_cast((vertex_id + 1u) % constants::n_elements); const auto edge = add_edge(vertex_id, target_id); auto row_view = sut.at(vertex_id); diff --git a/tests/source/gl/test_alg_dijkstra.cpp b/tests/source/gl/test_alg_dijkstra.cpp index 3bc04d1..b702648 100644 --- a/tests/source/gl/test_alg_dijkstra.cpp +++ b/tests/source/gl/test_alg_dijkstra.cpp @@ -69,7 +69,8 @@ TEST_CASE_TEMPLATE_DEFINE( const auto parent_id = id == 0u ? 0u : (id - 1u) / 2u; expected_predecessors.push_back(parent_id); - const auto vertex_depth = static_cast(std::floor(std::log2(id + 1uz))); + const auto vertex_depth = + static_cast(std::floor(std::log2(id + 1uz))); expected_distances.push_back(vertex_depth); } } @@ -184,7 +185,8 @@ TEST_CASE_TEMPLATE_DEFINE( const auto parent_id = id == 0u ? 0u : (id - 1u) / 2u; expected_predecessors.push_back(parent_id); - const auto vertex_depth = static_cast(std::floor(std::log2(id + 1uz))); + const auto vertex_depth = + static_cast(std::floor(std::log2(id + 1uz))); expected_distances.push_back(vertex_depth); } } diff --git a/tests/source/gl/test_graph.cpp b/tests/source/gl/test_graph.cpp index 8182d78..02ef2eb 100644 --- a/tests/source/gl/test_graph.cpp +++ b/tests/source/gl/test_graph.cpp @@ -89,7 +89,11 @@ struct test_graph { graph.vertex_ids(), [&graph, expected_n_edges = n_incident_edges_for_fully_connected_vertex(graph)]( const gl::default_id_type vertex_id - ) { return static_cast(gl::util::range_size(graph.adjacent_edges(vertex_id))) == expected_n_edges; } + ) { + return static_cast(gl::util::range_size(graph.adjacent_edges(vertex_id) + )) + == expected_n_edges; + } )); } @@ -216,7 +220,10 @@ TEST_CASE_TEMPLATE_DEFINE("graph structure tests", TraitsType, graph_traits_temp SUBCASE("get_vertex should throw if the given id is invalid") { sut_type sut{constants::n_elements}; - CHECK_THROWS_AS(static_cast(sut.get_vertex(static_cast(sut.order()))), std::out_of_range); + CHECK_THROWS_AS( + static_cast(sut.get_vertex(static_cast(sut.order()))), + std::out_of_range + ); } SUBCASE("get_vertex should return a vertex with the given id") { @@ -911,7 +918,8 @@ TEST_CASE_TEMPLATE_DEFINE("properties getter tests", TraitsType, property_graph_ sut_type sut{constants::n_elements}; for (auto vertex : sut.vertices()) { vertex.properties() = std::format("vertex_{}", vertex.id()); - const auto target_id = static_cast((vertex.id() + 1uz) % constants::n_elements); + const auto target_id = + static_cast((vertex.id() + 1uz) % constants::n_elements); sut.add_edge(vertex.id(), target_id).properties() = std::format("edge_{}", vertex.id()); } @@ -932,7 +940,10 @@ TEST_CASE_TEMPLATE_DEFINE("properties getter tests", TraitsType, property_graph_ for (auto [id, property] : std::views::enumerate(emap)) { CHECK_EQ(property, std::format("edge_{}", id)); CHECK_EQ(emap[id], std::format("edge_{}", id)); - CHECK_EQ(sut.get_edge_properties(static_cast(id)), std::format("edge_{}", id)); + CHECK_EQ( + sut.get_edge_properties(static_cast(id)), + std::format("edge_{}", id) + ); } CHECK_THROWS_AS( diff --git a/tests/source/gl/test_graph_topology_builders.cpp b/tests/source/gl/test_graph_topology_builders.cpp index 498c527..e47ca19 100644 --- a/tests/source/gl/test_graph_topology_builders.cpp +++ b/tests/source/gl/test_graph_topology_builders.cpp @@ -94,7 +94,8 @@ template using id_type = typename GraphType::id_type; using vertex_type = typename GraphType::vertex_type; return [&graph](const vertex_type& source) { - const auto prev_vertex_id = static_cast((source.id() + graph.order() - 1uz) % graph.order()); + const auto prev_vertex_id = + static_cast((source.id() + graph.order() - 1uz) % graph.order()); const auto prev_vertex = graph.get_vertex(prev_vertex_id); return std::ranges::all_of(graph.vertices(), [&](const auto& vertex) { @@ -111,7 +112,8 @@ template const auto next_vertex_id = static_cast((source.id() + 1uz) % graph.order()); const auto next_vertex = graph.get_vertex(next_vertex_id); - const auto prev_vertex_id = static_cast((source.id() + graph.order() - 1uz) % graph.order()); + const auto prev_vertex_id = + static_cast((source.id() + graph.order() - 1uz) % graph.order()); const auto prev_vertex = graph.get_vertex(prev_vertex_id); return std::ranges::all_of(graph.vertices(), [&](const auto& vertex) { diff --git a/tests/source/gl/test_util.cpp b/tests/source/gl/test_util.cpp index 1777a73..e3917fe 100644 --- a/tests/source/gl/test_util.cpp +++ b/tests/source/gl/test_util.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -55,6 +56,83 @@ TEST_CASE("upow_sum function test") { CHECK_EQ(gl::util::upow_sum(base, i_begin, i_end), expected_result); } +struct test_concat_view { + std::vector v1; + std::vector v2; + std::vector expected; +}; + +TEST_CASE_FIXTURE(test_concat_view, "concat should sequentially combine two identical range types") { + SUBCASE("Both ranges are non-empty") { + v1 = {1, 2, 3}; + v2 = {4, 5, 6}; + expected = {1, 2, 3, 4, 5, 6}; + } + SUBCASE("First range is empty") { + v1 = {}; + v2 = {4, 5, 6}; + expected = {4, 5, 6}; + } + SUBCASE("Second range is empty") { + v1 = {1, 2, 3}; + v2 = {}; + expected = {1, 2, 3}; + } + SUBCASE("Both ranges are empty") { + v1 = {}; + v2 = {}; + expected = {}; + } + + auto concat_vw = gl::util::concat(v1, v2); + + CHECK_EQ(std::ranges::distance(concat_vw), expected.size()); + CHECK(std::ranges::equal(concat_vw, expected)); + + const auto concat_vec = concat_vw | std::ranges::to(); + CHECK_EQ(concat_vec.size(), expected.size()); + CHECK(std::ranges::equal(concat_vec, expected)); +} + +TEST_CASE_FIXTURE(test_concat_view, "concat_view satisfies C++20 range concepts") { + v1 = {1, 2}; + v2 = {3, 4}; + + auto concat_vw = gl::util::concat(v1, v2); + + static_assert(std::ranges::view); + static_assert(std::ranges::forward_range); + + CHECK_EQ(std::ranges::distance(concat_vw), 4); +} + +TEST_CASE_FIXTURE(test_concat_view, "concat_view propagates const correctness") { + v1 = {10, 20}; + v2 = {30, 40}; + + const auto& cv1 = v1; + const auto& cv2 = v2; + + auto concat_vw = gl::util::concat(cv1, cv2); + + int sum = 0; + for (const auto& val : concat_vw) + sum += val; + + CHECK_EQ(sum, 100); +} + +TEST_CASE("concat should seamlessly bridge heterogeneous view types") { + auto vw1 = std::views::iota(1, 4); + std::vector vw2 = {4, 5, 6}; + + auto concat_vw = gl::util::concat(vw1, vw2); + std::vector expected = {1, 2, 3, 4, 5, 6}; + + CHECK_EQ(std::ranges::distance(concat_vw), expected.size()); + CHECK(std::ranges::equal(concat_vw, expected)); +} + TEST_SUITE_END(); // test_util } // namespace gl_testing From 55d00d4696b891656356fefa4bafae675fd56707 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Sat, 4 Apr 2026 00:57:07 +0200 Subject: [PATCH 5/7] clang alignment --- include/gl/attributes/diagnostics.hpp | 42 +++++++++ .../gl/impl/specialized/adjacency_list.hpp | 10 +- .../gl/impl/specialized/adjacency_matrix.hpp | 31 ++++-- .../impl/specialized/flat_adjacency_list.hpp | 5 +- .../specialized/flat_adjacency_matrix.hpp | 18 ++-- include/gl/types/flat_jagged_vector.hpp | 40 ++++---- include/gl/types/flat_matrix.hpp | 87 +++++++++++------ include/hgl/impl/flat_incidence_list.hpp | 9 +- include/hgl/impl/incidence_matrix.hpp | 2 +- tests/CMakeLists.txt | 1 + tests/include/testing/common/wrnsup.hpp | 2 + tests/source/gl/test_conversion.cpp | 2 - tests/source/gl/test_flat_jagged_vector.cpp | 4 +- tests/source/gl/test_flat_matrix.cpp | 94 ++++++++++++++++++- .../gl/test_graph_topology_builders.cpp | 17 ++-- 15 files changed, 279 insertions(+), 85 deletions(-) create mode 100644 include/gl/attributes/diagnostics.hpp diff --git a/include/gl/attributes/diagnostics.hpp b/include/gl/attributes/diagnostics.hpp new file mode 100644 index 0000000..fd04b01 --- /dev/null +++ b/include/gl/attributes/diagnostics.hpp @@ -0,0 +1,42 @@ +// Copyright (c) 2024-2026 Jakub MusiaƂ +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + +#pragma once + +#define GL_PRAGMA(x) _Pragma(#x) + +#if defined(__clang__) + +#define GL_SUPPRESS_WARNING_BEGIN(w) \ + GL_PRAGMA(clang diagnostic push) \ + GL_PRAGMA(clang diagnostic ignored w) + +#define GL_SUPPRESS_WARNING_END GL_PRAGMA(clang diagnostic pop) + +#define GL_SUPPRESS_CLANG_WARNING_BEGIN(w) GL_SUPPRESS_WARNING_BEGIN(w) + +// GCC-only warning: Push on Clang to keep the END macro balanced, but don't ignore +#define GL_SUPPRESS_GCC_WARNING_BEGIN(w) GL_PRAGMA(clang diagnostic push) + +#elif defined(__GNUC__) + +#define GL_SUPPRESS_WARNING_BEGIN(w) \ + GL_PRAGMA(GCC diagnostic push) \ + GL_PRAGMA(GCC diagnostic ignored w) + +#define GL_SUPPRESS_WARNING_END GL_PRAGMA(GCC diagnostic pop) + +#define GL_SUPPRESS_GCC_WARNING_BEGIN(w) GL_SUPPRESS_WARNING_BEGIN(w) + +// Clang-only warning: Push on GCC to keep the END macro balanced, but don't ignore +#define GL_SUPPRESS_CLANG_WARNING_BEGIN(w) GL_PRAGMA(GCC diagnostic push) + +#else + +#define GL_SUPPRESS_WARNING_BEGIN(w) +#define GL_SUPPRESS_WARNING_END +#define GL_SUPPRESS_GCC_WARNING_BEGIN(w) +#define GL_SUPPRESS_CLANG_WARNING_BEGIN(w) + +#endif diff --git a/include/gl/impl/specialized/adjacency_list.hpp b/include/gl/impl/specialized/adjacency_list.hpp index f0c6964..4e680af 100644 --- a/include/gl/impl/specialized/adjacency_list.hpp +++ b/include/gl/impl/specialized/adjacency_list.hpp @@ -8,8 +8,10 @@ #include "gl/decl/impl_tags.hpp" #include "gl/graph_traits.hpp" #include "gl/traits.hpp" +#include "gl/types/core.hpp" #include +#include #include #include #include @@ -77,7 +79,9 @@ struct directed_adjacency_list { [[nodiscard]] static size_type in_degree(const impl_type& self, id_type vertex_id) { size_type in_deg = 0uz; for (const auto& adjacent_edges : self._list) - in_deg += std::ranges::count(adjacent_edges, vertex_id, &item_type::vertex_id); + in_deg += static_cast( + std::ranges::count(adjacent_edges, vertex_id, &item_type::vertex_id) + ); return in_deg; } @@ -150,7 +154,7 @@ struct directed_adjacency_list { } // remove the list of edges incident from the vertex entirely - self._list.erase(std::next(std::begin(self._list), vertex_idx)); + self._list.erase(self._list.begin() + static_cast(vertex_id)); return removed_edges; } @@ -251,7 +255,7 @@ struct undirected_adjacency_list { const auto removed_edges = self._list[vertex_idx] | std::views::transform(&item_type::edge_id) | std::ranges::to(); - self._list.erase(std::next(std::begin(self._list), vertex_idx)); + self._list.erase(self._list.begin() + static_cast(vertex_id)); return removed_edges; } diff --git a/include/gl/impl/specialized/adjacency_matrix.hpp b/include/gl/impl/specialized/adjacency_matrix.hpp index 9fe0dab..ec6ebcc 100644 --- a/include/gl/impl/specialized/adjacency_matrix.hpp +++ b/include/gl/impl/specialized/adjacency_matrix.hpp @@ -4,12 +4,14 @@ #pragma once +#include "gl/attributes/diagnostics.hpp" #include "gl/constants.hpp" #include "gl/decl/impl_tags.hpp" #include "gl/graph_traits.hpp" #include "gl/types/core.hpp" #include +#include #include namespace gl::impl { @@ -76,19 +78,28 @@ struct directed_adjacency_matrix { self._matrix.emplace_back(new_n_vertices, invalid_id); } + GL_SUPPRESS_WARNING_BEGIN("-Wsign-conversion") + + // NOTE: Indexing into a row which might be a vector (requires size type) or a span/subrange (requires difference type) + [[nodiscard]] gl_attr_force_inline static size_type in_degree( const impl_type& self, id_type vertex_id ) { - return std::ranges::count_if(self._matrix, [vertex_id](const auto& row) { - return row[to_idx(vertex_id)] != invalid_id; - }); + return static_cast(std::ranges::count_if( + self._matrix, + [vertex_id](const auto& row) { return row[to_idx(vertex_id)] != invalid_id; } + )); } + GL_SUPPRESS_WARNING_END + [[nodiscard]] gl_attr_force_inline static size_type out_degree( const impl_type& self, id_type vertex_id ) { return self._matrix[vertex_id].size() - - std::ranges::count(self._matrix[vertex_id], invalid_id_v); + - static_cast( + std::ranges::count(self._matrix[vertex_id], invalid_id_v) + ); } [[nodiscard]] gl_attr_force_inline static size_type degree( @@ -143,12 +154,12 @@ struct directed_adjacency_matrix { | std::views::filter([](auto edge_id) { return edge_id != invalid_id; }) | std::ranges::to(); - self._matrix.erase(std::next(std::begin(self._matrix), vertex_idx)); + self._matrix.erase(self._matrix.begin() + static_cast(vertex_id)); for (auto& row : self._matrix) { if (const auto edge_id = row[vertex_idx]; edge_id != invalid_id) removed_edges.push_back(edge_id); - row.erase(std::next(std::begin(row), vertex_idx)); + row.erase(row.begin() + static_cast(vertex_id)); } return removed_edges; @@ -225,7 +236,9 @@ struct undirected_adjacency_matrix { ) { const auto vertex_idx = to_idx(vertex_id); return self._matrix.size() - - std::ranges::count(self._matrix[vertex_idx], invalid_id_v) + - static_cast( + std::ranges::count(self._matrix[vertex_idx], invalid_id_v) + ) + static_cast(self._matrix[vertex_idx][vertex_idx] != invalid_id); } @@ -264,9 +277,9 @@ struct undirected_adjacency_matrix { | std::views::filter([](auto edge_id) { return edge_id != invalid_id; }) | std::ranges::to(); - self._matrix.erase(std::next(std::begin(self._matrix), vertex_idx)); + self._matrix.erase(self._matrix.begin() + static_cast(vertex_id)); for (auto& row : self._matrix) - row.erase(std::next(std::begin(row), vertex_idx)); + row.erase(row.begin() + static_cast(vertex_id)); return removed_edges; } diff --git a/include/gl/impl/specialized/flat_adjacency_list.hpp b/include/gl/impl/specialized/flat_adjacency_list.hpp index cdd1ea9..b1e41bd 100644 --- a/include/gl/impl/specialized/flat_adjacency_list.hpp +++ b/include/gl/impl/specialized/flat_adjacency_list.hpp @@ -8,6 +8,7 @@ #include "gl/decl/impl_tags.hpp" #include "gl/graph_traits.hpp" #include "gl/impl/specialized/adjacency_list.hpp" +#include "gl/types/core.hpp" #include "gl/types/flat_jagged_vector.hpp" #include @@ -42,7 +43,9 @@ struct directed_flat_adjacency_list { } [[nodiscard]] static size_type in_degree(const impl_type& self, id_type vertex_id) { - return std::ranges::count(self._list.data_view(), vertex_id, &item_type::vertex_id); + return static_cast( + std::ranges::count(self._list.data_view(), vertex_id, &item_type::vertex_id) + ); } [[nodiscard]] gl_attr_force_inline static size_type out_degree( diff --git a/include/gl/impl/specialized/flat_adjacency_matrix.hpp b/include/gl/impl/specialized/flat_adjacency_matrix.hpp index 1d7adcc..74394ea 100644 --- a/include/gl/impl/specialized/flat_adjacency_matrix.hpp +++ b/include/gl/impl/specialized/flat_adjacency_matrix.hpp @@ -12,6 +12,7 @@ #include "gl/types/flat_matrix.hpp" #include +#include #include #include #include @@ -45,16 +46,16 @@ struct directed_flat_adjacency_matrix { [[nodiscard]] gl_attr_force_inline static size_type in_degree( const impl_type& self, id_type vertex_id ) { - return std::ranges::count_if(self._matrix.col(to_idx(vertex_id)), [](auto edge_id) { - return edge_id != invalid_id; - }); + return static_cast(std::ranges::count_if( + self._matrix.col(to_idx(vertex_id)), [](auto edge_id) { return edge_id != invalid_id; } + )); } [[nodiscard]] gl_attr_force_inline static size_type out_degree( const impl_type& self, id_type vertex_id ) { const auto row = self._matrix[to_idx(vertex_id)]; - return row.size() - std::ranges::count(row, invalid_id_v); + return row.size() - static_cast(std::ranges::count(row, invalid_id_v)); } [[nodiscard]] gl_attr_force_inline static size_type degree( @@ -67,7 +68,7 @@ struct directed_flat_adjacency_matrix { for (auto v_idx = 0uz; v_idx < self._matrix.n_rows(); ++v_idx) deg += static_cast(row[v_idx] != invalid_id) - + static_cast(col[v_idx] != invalid_id); + + static_cast(col[static_cast(v_idx)] != invalid_id); return deg; } @@ -119,7 +120,9 @@ struct directed_flat_adjacency_matrix { for (auto r_idx = 0uz; r_idx < self._matrix.n_rows(); ++r_idx) { if (r_idx == vertex_idx) continue; - if (const auto edge_id = col[r_idx]; edge_id != invalid_id) + + const auto edge_id = col[static_cast(r_idx)]; + if (edge_id != invalid_id) removed_edges.push_back(edge_id); } @@ -197,7 +200,8 @@ struct undirected_flat_adjacency_matrix { ) { const auto vertex_idx = to_idx(vertex_id); const auto row = self._matrix[vertex_idx]; - return self._matrix.n_cols() - std::ranges::count(row, invalid_id_v) + return self._matrix.n_cols() + - static_cast(std::ranges::count(row, invalid_id_v)) + static_cast(self._matrix[vertex_idx, vertex_idx] != invalid_id); } diff --git a/include/gl/types/flat_jagged_vector.hpp b/include/gl/types/flat_jagged_vector.hpp index 4d2e180..9687f62 100644 --- a/include/gl/types/flat_jagged_vector.hpp +++ b/include/gl/types/flat_jagged_vector.hpp @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -29,6 +30,7 @@ namespace gl { /// @todo Implement assign, and swap methods. /// @todo Implement iterator-based insert, emplace and erase methods. /// @todo Add `operator<<` overload for `std::ostream` and specialize `std::formatter`. +/// @todo Use `std::ptrdiff_t` instead of `std::size_t` for offset values. template class flat_jagged_vector { public: @@ -903,6 +905,7 @@ class flat_jagged_vector { requires std::convertible_to, value_type> void insert(size_type pos, R&& r) { const auto beg = this->_offsets[pos]; + const auto beg_pos = static_cast(beg); const auto old_size = this->_data.size(); this->_ensure_offset_capacity(); @@ -910,16 +913,16 @@ class flat_jagged_vector { if constexpr (std::ranges::contiguous_range) { auto* ptr = std::ranges::data(r); const auto n = std::ranges::size(r); - this->_data.insert(this->_data.begin() + beg, ptr, ptr + n); + this->_data.insert(this->_data.begin() + beg_pos, ptr, ptr + n); } else { this->_data.insert( - this->_data.begin() + beg, std::ranges::begin(r), std::ranges::end(r) + this->_data.begin() + beg_pos, std::ranges::begin(r), std::ranges::end(r) ); } const auto inserted = this->_data.size() - old_size; - this->_offsets.insert(this->_offsets.begin() + pos, beg); + this->_offsets.insert(this->_offsets.begin() + static_cast(pos), beg); for (size_type i = pos + 1uz; i < this->_offsets.size(); i++) this->_offsets[i] += inserted; } @@ -949,12 +952,12 @@ class flat_jagged_vector { /// the erased segment in the underlying vector, $S$ is the number of segments after `pos`, /// and $L$ is the size of the erased segment. Erasing the **last** segment is $O(L)$. void erase(size_type pos) { - const auto start = this->_offsets[pos]; - const auto end = this->_offsets[pos + 1uz]; - const auto len = end - start; + const auto start = static_cast(this->_offsets[pos]); + const auto end = static_cast(this->_offsets[pos + 1uz]); + const auto len = static_cast(end - start); this->_data.erase(this->_data.begin() + start, this->_data.begin() + end); - this->_offsets.erase(this->_offsets.begin() + pos); + this->_offsets.erase(this->_offsets.begin() + static_cast(pos)); for (size_type i = pos; i < this->_offsets.size(); i++) this->_offsets[i] -= len; } @@ -1019,7 +1022,8 @@ class flat_jagged_vector { /// @note **Time Complexity:** Amortized $O(E + S)$ where $E$ is the number of elements after /// the insertion point in the underlying vector, and $S$ is the number of segments after `seg`. void insert(size_type seg, size_type pos, const value_type& value) { - this->_data.insert(this->_data.begin() + this->_offsets[seg] + pos, value); + const auto insert_pos = static_cast(this->_offsets[seg] + pos); + this->_data.insert(this->_data.begin() + insert_pos, value); for (size_type i = seg + 1uz; i < this->_offsets.size(); i++) this->_offsets[i]++; } @@ -1037,9 +1041,8 @@ class flat_jagged_vector { /// the insertion point in the underlying vector, and $S$ is the number of segments after `seg`. template void emplace(size_type seg, size_type pos, Args&&... args) { - this->_data.emplace( - this->_data.begin() + this->_offsets[seg] + pos, std::forward(args)... - ); + const auto insert_pos = static_cast(this->_offsets[seg] + pos); + this->_data.emplace(this->_data.begin() + insert_pos, std::forward(args)...); for (size_type i = seg + 1uz; i < this->_offsets.size(); i++) this->_offsets[i]++; } @@ -1052,7 +1055,8 @@ class flat_jagged_vector { /// @note **Time Complexity:** $O(E + S)$ where $E$ is the number of elements after the erased /// position in the underlying vector, and $S$ is the number of segments after `seg`. void erase(size_type seg, size_type pos) { - this->_data.erase(this->_data.begin() + this->_offsets[seg] + pos); + const auto erase_pos = static_cast(this->_offsets[seg] + pos); + this->_data.erase(this->_data.begin() + erase_pos); for (size_type i = seg + 1uz; i < this->_offsets.size(); i++) this->_offsets[i]--; } @@ -1076,8 +1080,8 @@ class flat_jagged_vector { const auto curr_count = this->segment_size(seg); if (n < curr_count) { const auto diff = curr_count - n; - const auto start = this->_offsets[seg] + n; - const auto end = this->_offsets[seg + 1uz]; + const auto start = static_cast(this->_offsets[seg] + n); + const auto end = static_cast(this->_offsets[seg + 1uz]); this->_data.erase(this->_data.begin() + start, this->_data.begin() + end); for (size_type i = seg + 1uz; i < this->_offsets.size(); i++) @@ -1085,7 +1089,7 @@ class flat_jagged_vector { } else if (n > curr_count) { const auto diff = n - curr_count; - const auto pos = this->_offsets[seg + 1uz]; + const auto pos = static_cast(this->_offsets[seg + 1uz]); this->_data.insert(this->_data.begin() + pos, diff, value_type()); for (size_type i = seg + 1uz; i < this->_offsets.size(); i++) @@ -1113,8 +1117,8 @@ class flat_jagged_vector { const auto curr_count = this->segment_size(seg); if (n < curr_count) { const auto diff = curr_count - n; - const auto start = this->_offsets[seg] + n; - const auto end = this->_offsets[seg + 1uz]; + const auto start = static_cast(this->_offsets[seg] + n); + const auto end = static_cast(this->_offsets[seg + 1uz]); this->_data.erase(this->_data.begin() + start, this->_data.begin() + end); for (size_type i = seg + 1uz; i < this->_offsets.size(); i++) @@ -1122,7 +1126,7 @@ class flat_jagged_vector { } else if (n > curr_count) { const auto diff = n - curr_count; - const auto pos = this->_offsets[seg + 1uz]; + const auto pos = static_cast(this->_offsets[seg + 1uz]); this->_data.insert(this->_data.begin() + pos, diff, value); for (size_type i = seg + 1uz; i < this->_offsets.size(); i++) diff --git a/include/gl/types/flat_matrix.hpp b/include/gl/types/flat_matrix.hpp index f7a033b..0265098 100644 --- a/include/gl/types/flat_matrix.hpp +++ b/include/gl/types/flat_matrix.hpp @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -137,7 +138,7 @@ class flat_matrix { /// @param n Number of rows to advance (can be negative) /// @return Reference to this iterator row_iterator& operator+=(difference_type n) noexcept { - this->_row_idx += n; + this->_row_idx += static_cast(n); return *this; } @@ -145,7 +146,7 @@ class flat_matrix { /// @param n Number of rows to move backward (can be negative) /// @return Reference to this iterator row_iterator& operator-=(difference_type n) noexcept { - this->_row_idx -= n; + this->_row_idx -= static_cast(n); return *this; } @@ -180,7 +181,7 @@ class flat_matrix { [[nodiscard]] friend difference_type operator-( const row_iterator& lhs, const row_iterator& rhs ) noexcept { - return lhs._row_idx - rhs._row_idx; + return static_cast(lhs._row_idx - rhs._row_idx); } /// @brief Tests equality of two iterators. @@ -776,10 +777,15 @@ class flat_matrix { } /// @brief Appends a newly created row filled with a specific value. + /// + /// If the matrix is empty (has no columns), this operation has no effect, + /// as the size of the new row will be determined as 0. + /// /// @param value The value to fill the new row with /// @post `n_rows()` increases by 1 /// @exception std::bad_alloc If memory allocation fails /// @warning Invalidates all iterators, pointers, and references if reallocation occurs. + /// @note If the matrix is empty, nothing will happen. /// @note **Time Complexity:** Amortized $O(C)$ where $C$ is the number of columns. void push_row(const value_type& value) { this->insert_row(this->_n_rows, value); @@ -808,7 +814,7 @@ class flat_matrix { )); } - const auto insert_idx = pos * this->_n_cols; + const auto insert_pos = static_cast(pos * this->_n_cols); if constexpr (std::ranges::sized_range) { const auto row_size = static_cast(std::ranges::size(r)); @@ -825,11 +831,11 @@ class flat_matrix { if constexpr (std::ranges::contiguous_range) { auto* ptr = std::ranges::data(r); - this->_data.insert(this->_data.begin() + insert_idx, ptr, ptr + row_size); + this->_data.insert(this->_data.begin() + insert_pos, ptr, ptr + row_size); } else { this->_data.insert( - this->_data.begin() + insert_idx, std::ranges::begin(r), std::ranges::end(r) + this->_data.begin() + insert_pos, std::ranges::begin(r), std::ranges::end(r) ); } } @@ -838,13 +844,13 @@ class flat_matrix { const auto old_size = this->_data.size(); this->_data.insert( - this->_data.begin() + insert_idx, std::ranges::begin(r), std::ranges::end(r) + this->_data.begin() + insert_pos, std::ranges::begin(r), std::ranges::end(r) ); const auto row_size = this->_data.size() - old_size; if (this->_n_rows > 0uz and row_size != this->_n_cols) { this->_data.erase( - this->_data.begin() + insert_idx, this->_data.begin() + insert_idx + row_size + this->_data.begin() + insert_pos, this->_data.begin() + insert_pos + row_size ); throw std::invalid_argument(std::format( "flat_matrix::insert_row: row size mismatch (expected {}, got {})", @@ -875,6 +881,10 @@ class flat_matrix { } /// @brief Inserts a newly created row filled with a specific value at the specified position. + /// + /// If the matrix is empty (has no columns), this operation has no effect, + /// as the size of the new row will be determined as 0. + /// /// @param pos The row position where the elements will be inserted /// @param value The value to fill the new row with /// @post `n_rows()` increases by 1; rows at and after `pos` are shifted down @@ -892,7 +902,8 @@ class flat_matrix { )); } - this->_data.insert(this->_data.begin() + (pos * this->_n_cols), this->_n_cols, value); + const auto insert_pos = static_cast(pos * this->_n_cols); + this->_data.insert(this->_data.begin() + insert_pos, this->_n_cols, value); ++this->_n_rows; } @@ -926,8 +937,9 @@ class flat_matrix { return; } - const auto start_it = this->_data.begin() + (pos * this->_n_cols); - this->_data.erase(start_it, start_it + this->_n_cols); + const auto start_it = + this->_data.begin() + static_cast(pos * this->_n_cols); + this->_data.erase(start_it, start_it + static_cast(this->_n_cols)); --this->_n_rows; } @@ -961,6 +973,10 @@ class flat_matrix { } /// @brief Appends a newly created column filled with a specific value at the right edge. + /// + /// If the matrix is empty (has no rows), this operation has no effect, + /// as the size of the new column will be determined as 0. + /// /// @param value The value to fill the new column with /// @post `n_cols()` increases by 1 /// @exception std::bad_alloc If memory allocation fails @@ -1011,22 +1027,25 @@ class flat_matrix { new_data.reserve(this->_n_rows * (this->_n_cols + 1uz)); auto r_it = std::ranges::begin(r); - for (size_type r_idx = 0uz; r_idx < this->_n_rows; ++r_idx) { - auto row_begin = this->_data.begin() + r_idx * this->_n_cols; + const auto n_rows_bound = static_cast(this->_n_rows); + const auto row_size = static_cast(this->_n_cols); + const auto c_pos = static_cast(pos); + for (auto r_pos = 0z; r_pos < n_rows_bound; ++r_pos) { + auto row_begin = this->_data.begin() + r_pos * row_size; // move old row elements up to insertion point new_data.insert( new_data.end(), std::make_move_iterator(row_begin), - std::make_move_iterator(row_begin + pos) + std::make_move_iterator(row_begin + c_pos) ); // insert new column element new_data.push_back(*r_it++); // move the remainder of old row new_data.insert( new_data.end(), - std::make_move_iterator(row_begin + pos), - std::make_move_iterator(row_begin + this->_n_cols) + std::make_move_iterator(row_begin + c_pos), + std::make_move_iterator(row_begin + row_size) ); } @@ -1055,6 +1074,10 @@ class flat_matrix { } /// @brief Inserts a newly created column filled with a specific value at the specified position. + /// + /// If the matrix is empty (has no rows), this operation has no effect, + /// as the size of the new column will be determined as 0. + /// /// @param pos The column position where elements will be inserted /// @param value The value to fill the new column with /// @post `n_cols()` increases by 1 @@ -1075,19 +1098,22 @@ class flat_matrix { std::vector new_data; new_data.reserve(this->_n_rows * (this->_n_cols + 1uz)); - for (size_type r_idx = 0uz; r_idx < this->_n_rows; ++r_idx) { - auto row_begin = this->_data.begin() + r_idx * this->_n_cols; + const auto n_rows_bound = static_cast(this->_n_rows); + const auto row_size = static_cast(this->_n_cols); + const auto c_pos = static_cast(pos); + for (auto r_pos = 0z; r_pos < n_rows_bound; ++r_pos) { + auto row_begin = this->_data.begin() + r_pos * row_size; new_data.insert( new_data.end(), std::make_move_iterator(row_begin), - std::make_move_iterator(row_begin + pos) + std::make_move_iterator(row_begin + c_pos) ); new_data.push_back(value); // Insert the fill value new_data.insert( new_data.end(), - std::make_move_iterator(row_begin + pos), - std::make_move_iterator(row_begin + this->_n_cols) + std::make_move_iterator(row_begin + c_pos), + std::make_move_iterator(row_begin + row_size) ); } @@ -1124,18 +1150,21 @@ class flat_matrix { std::vector new_data; new_data.reserve(this->_n_rows * (this->_n_cols - 1uz)); - for (size_type r_idx = 0uz; r_idx < this->_n_rows; ++r_idx) { - auto row_begin = this->_data.begin() + r_idx * this->_n_cols; + const auto n_rows_bound = static_cast(this->_n_rows); + const auto row_size = static_cast(this->_n_cols); + const auto c_pos = static_cast(pos); + for (auto r_pos = 0z; r_pos < n_rows_bound; ++r_pos) { + auto row_begin = this->_data.begin() + r_pos * row_size; new_data.insert( new_data.end(), std::make_move_iterator(row_begin), - std::make_move_iterator(row_begin + pos) + std::make_move_iterator(row_begin + c_pos) ); new_data.insert( new_data.end(), - std::make_move_iterator(row_begin + pos + 1uz), - std::make_move_iterator(row_begin + this->_n_cols) + std::make_move_iterator(row_begin + c_pos + 1z), + std::make_move_iterator(row_begin + row_size) ); } @@ -1275,14 +1304,16 @@ class flat_matrix { /// @param c The column index (assumed valid) /// @return A zero-overhead `std::views::stride` representing the column elements [[nodiscard]] auto _col_impl(size_type c) noexcept { - return std::views::drop(this->_data, c) | std::views::stride(this->_n_cols); + return std::views::drop(this->_data, static_cast(c)) + | std::views::stride(this->_n_cols); } /// @brief Internal non-throwing helper generating a const strided view over a column. /// @param c The column index (assumed valid) /// @return A zero-overhead `std::views::stride` representing the const column elements [[nodiscard]] auto _col_impl(size_type c) const noexcept { - return std::views::drop(this->_data, c) | std::views::stride(this->_n_cols); + return std::views::drop(this->_data, static_cast(c)) + | std::views::stride(this->_n_cols); } size_type _n_rows{0uz}; diff --git a/include/hgl/impl/flat_incidence_list.hpp b/include/hgl/impl/flat_incidence_list.hpp index 3260d36..bbbc859 100644 --- a/include/hgl/impl/flat_incidence_list.hpp +++ b/include/hgl/impl/flat_incidence_list.hpp @@ -559,11 +559,10 @@ class flat_incidence_list final { } else { // get minor return std::views::iota(initial_id_v, this->_tail_storage.size()) - | std::views::filter( - [this, &storage = this->*storage_proj, minor_id = id](id_type major_id) { - return detail::contains(storage[to_idx(major_id)], minor_id); - } - ); + | std::views::filter([&storage = this->*storage_proj, + minor_id = id](id_type major_id) { + return detail::contains(storage[to_idx(major_id)], minor_id); + }); } } diff --git a/include/hgl/impl/incidence_matrix.hpp b/include/hgl/impl/incidence_matrix.hpp index 9913c48..795b270 100644 --- a/include/hgl/impl/incidence_matrix.hpp +++ b/include/hgl/impl/incidence_matrix.hpp @@ -217,7 +217,7 @@ class incidence_matrix final { if constexpr (Element == layout_tag::major_element) { // count map major const size_type limit = std::min(n_elements, this->_matrix.size()); for (auto i = 0uz; i < limit; ++i) { - size_map[i] = std::ranges::count(this->_matrix[i], true); + size_map[i] = static_cast(std::ranges::count(this->_matrix[i], true)); } } else { // count map minor diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d116409..05acebe 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -30,6 +30,7 @@ function(add_test_target TARGET_NAME SOURCE_DIRS DATA_DIR TESTING_DEF) -Wextra -Wcast-align -Wconversion + -Wsign-conversion -Wunreachable-code -Wuninitialized -Wunused diff --git a/tests/include/testing/common/wrnsup.hpp b/tests/include/testing/common/wrnsup.hpp index fb1a406..a89fe9a 100644 --- a/tests/include/testing/common/wrnsup.hpp +++ b/tests/include/testing/common/wrnsup.hpp @@ -1,3 +1,5 @@ +// TODO: delete + #pragma once #if defined(__clang__) || defined(__GNUC__) diff --git a/tests/source/gl/test_conversion.cpp b/tests/source/gl/test_conversion.cpp index 3edcebc..cca8758 100644 --- a/tests/source/gl/test_conversion.cpp +++ b/tests/source/gl/test_conversion.cpp @@ -19,8 +19,6 @@ namespace gl_testing { TEST_SUITE_BEGIN("test_conversion"); -constexpr auto get_id = [](auto&& element) -> gl::default_id_type { return element.id(); }; - struct test_conversion { using property_type = gl::name_property; diff --git a/tests/source/gl/test_flat_jagged_vector.cpp b/tests/source/gl/test_flat_jagged_vector.cpp index c36df3c..482ca3a 100644 --- a/tests/source/gl/test_flat_jagged_vector.cpp +++ b/tests/source/gl/test_flat_jagged_vector.cpp @@ -1291,9 +1291,9 @@ TEST_CASE_FIXTURE( CHECK_EQ(sut.size(), 100uz); CHECK_EQ(sut.data_size(), 1000uz); for (int i = 0; i < 100; ++i) { - auto seg = sut[i]; + auto seg = sut[static_cast(i)]; for (int j = 0; j < 10; ++j) - CHECK_EQ(seg[j], i * 10 + j); + CHECK_EQ(seg[static_cast(j)], i * 10 + j); } } diff --git a/tests/source/gl/test_flat_matrix.cpp b/tests/source/gl/test_flat_matrix.cpp index 912c44f..3e0a93e 100644 --- a/tests/source/gl/test_flat_matrix.cpp +++ b/tests/source/gl/test_flat_matrix.cpp @@ -1001,6 +1001,25 @@ TEST_CASE_FIXTURE( CHECK(std::ranges::equal(sut[2uz], sut_row2)); } +TEST_CASE_FIXTURE(test_flat_matrix_row_modifiers, "push_row with value should add new row") { + sut.push_row({1, 2, 3}); + sut.push_row(4); + + CHECK_EQ(sut.n_rows(), 2uz); + CHECK_EQ(sut.n_cols(), 3uz); + CHECK_EQ(sut.data_size(), 6uz); + CHECK(std::ranges::equal(sut[1uz], std::vector{4, 4, 4})); +} + +TEST_CASE_FIXTURE( + test_flat_matrix_row_modifiers, "push_row with value should do nothing if the matrix is empty" +) { + REQUIRE(sut.empty()); + + sut.push_row(0); + CHECK(sut.empty()); +} + TEST_CASE_FIXTURE(test_flat_matrix_row_modifiers, "pop_row should remove last row") { sut.push_row(sut_row0); sut.push_row(sut_row1); @@ -1087,6 +1106,32 @@ TEST_CASE_FIXTURE( CHECK_THROWS_AS(sut.insert_row(1uz, {11, 22}), std::invalid_argument); } +TEST_CASE_FIXTURE(test_flat_matrix_row_modifiers, "insert_row with value should add new row") { + sut.push_row({1, 2, 3}); + sut.insert_row(1uz, 4); + + CHECK_EQ(sut.n_rows(), 2uz); + CHECK_EQ(sut.n_cols(), 3uz); + CHECK_EQ(sut.data_size(), 6uz); + CHECK(std::ranges::equal(sut[1uz], std::vector{4, 4, 4})); +} + +TEST_CASE_FIXTURE( + test_flat_matrix_row_modifiers, "insert_row with value should do nothing if the matrix is empty" +) { + REQUIRE(sut.empty()); + + sut.insert_row(0uz, 0); + CHECK(sut.empty()); +} + +TEST_CASE_FIXTURE( + test_flat_matrix_row_modifiers, "insert_row with value should throw for invalid position" +) { + sut.push_row({1, 2, 3}); + CHECK_THROWS_AS(sut.insert_row(2uz, 4), std::out_of_range); +} + TEST_CASE_FIXTURE(test_flat_matrix_row_modifiers, "erase_row should remove row at given position") { sut.push_row(sut_row0); sut.push_row(sut_row1); @@ -1177,6 +1222,25 @@ TEST_CASE_FIXTURE( CHECK(std::ranges::equal(sut.col(2uz), sut_col2)); } +TEST_CASE_FIXTURE(test_flat_matrix_row_modifiers, "push_col with value should add new column") { + sut.push_col({1, 2, 3}); + sut.push_col(4); + + CHECK_EQ(sut.n_rows(), 3uz); + CHECK_EQ(sut.n_cols(), 2uz); + CHECK_EQ(sut.data_size(), 6uz); + CHECK(std::ranges::equal(sut.col(1uz), std::vector{4, 4, 4})); +} + +TEST_CASE_FIXTURE( + test_flat_matrix_row_modifiers, "push_col with value should do nothing if the matrix is empty" +) { + REQUIRE(sut.empty()); + + sut.push_col(0); + CHECK(sut.empty()); +} + TEST_CASE_FIXTURE(test_flat_matrix_col_modifiers, "pop_col should remove last column") { sut.push_col(sut_col0); sut.push_col(sut_col1); @@ -1263,6 +1327,32 @@ TEST_CASE_FIXTURE( CHECK_THROWS_AS(sut.insert_col(1uz, {11, 22}), std::invalid_argument); } +TEST_CASE_FIXTURE(test_flat_matrix_row_modifiers, "insert_col with value should add new col") { + sut.push_col({1, 2, 3}); + sut.insert_col(1uz, 4); + + CHECK_EQ(sut.n_rows(), 3uz); + CHECK_EQ(sut.n_cols(), 2uz); + CHECK_EQ(sut.data_size(), 6uz); + CHECK(std::ranges::equal(sut.col(1uz), std::vector{4, 4, 4})); +} + +TEST_CASE_FIXTURE( + test_flat_matrix_row_modifiers, "insert_col with value should do nothing if the matrix is empty" +) { + REQUIRE(sut.empty()); + + sut.insert_col(0uz, 0); + CHECK(sut.empty()); +} + +TEST_CASE_FIXTURE( + test_flat_matrix_row_modifiers, "insert_col with value should throw for invalid position" +) { + sut.push_col({1, 2, 3}); + CHECK_THROWS_AS(sut.insert_col(2uz, 4), std::out_of_range); +} + TEST_CASE_FIXTURE( test_flat_matrix_col_modifiers, "erase_col should remove column at given position" ) { @@ -1357,9 +1447,9 @@ TEST_CASE_FIXTURE( CHECK_EQ(sut.n_cols(), 10uz); CHECK_EQ(sut.data_size(), 1000uz); for (int i = 0; i < 100; ++i) { - auto row = sut[i]; + auto row = sut[static_cast(i)]; for (int j = 0; j < 10; ++j) - CHECK_EQ(row[j], i * 10 + j); + CHECK_EQ(row[static_cast(j)], i * 10 + j); } } diff --git a/tests/source/gl/test_graph_topology_builders.cpp b/tests/source/gl/test_graph_topology_builders.cpp index e47ca19..bbcf6d2 100644 --- a/tests/source/gl/test_graph_topology_builders.cpp +++ b/tests/source/gl/test_graph_topology_builders.cpp @@ -4,6 +4,8 @@ #include +#include + namespace gl_testing { TEST_SUITE_BEGIN("test_graph_topology_builders"); @@ -283,6 +285,7 @@ TEST_CASE_TEMPLATE_DEFINE( SUBCASE("path(n_vertices) should build a one-way path graph of size n_vertices") { const auto path = gl::topology::path(constants::n_elements_top); const auto n_source_vertices = path.order() - 1uz; + const auto last_vertex_pos = static_cast(n_source_vertices); verify_graph_size(path, constants::n_elements_top, n_source_vertices); @@ -290,12 +293,13 @@ TEST_CASE_TEMPLATE_DEFINE( path.vertices() | std::views::take(n_source_vertices), predicate::is_vertex_connected_to_next_only(path) )); - CHECK(predicate::is_vertex_not_connected(path)(path.vertices()[n_source_vertices])); + CHECK(predicate::is_vertex_not_connected(path)(path.vertices()[last_vertex_pos])); } SUBCASE("bidirectional_path(n_vertices) should build a two-way path graph of size n_vertices") { const auto path = gl::topology::bidirectional_path(constants::n_elements_top); const auto n_source_vertices = path.order() - 1uz; + const auto last_vertex_pos = static_cast(n_source_vertices); verify_graph_size(path, constants::n_elements_top, 2uz * n_source_vertices); @@ -303,12 +307,11 @@ TEST_CASE_TEMPLATE_DEFINE( REQUIRE(std::ranges::all_of( std::ranges::next(vertices.begin(), 1uz), - std::ranges::next(vertices.begin(), n_source_vertices), + std::ranges::next(vertices.begin(), last_vertex_pos), predicate::is_vertex_connected_to_id_adjacent(path) )); CHECK(predicate::is_vertex_connected_to_next_only(path)(*path.vertices().begin())); - CHECK(predicate::is_vertex_connected_to_prev_only(path)(path.vertices()[n_source_vertices]) - ); + CHECK(predicate::is_vertex_connected_to_prev_only(path)(path.vertices()[last_vertex_pos])); } SUBCASE("regular_binary_tree(depth) should return a one-way regular binay tree with the " @@ -386,18 +389,18 @@ TEST_CASE_TEMPLATE_DEFINE( CAPTURE(path); const auto n_source_vertices = path.order() - 1uz; + const auto last_vertex_pos = static_cast(n_source_vertices); verify_graph_size(path, constants::n_elements_top, n_source_vertices); const auto vertices = path.vertices(); REQUIRE(std::ranges::all_of( std::ranges::next(vertices.begin(), 1uz), - std::ranges::next(vertices.begin(), n_source_vertices), + std::ranges::next(vertices.begin(), last_vertex_pos), predicate::is_vertex_connected_to_id_adjacent(path) )); CHECK(predicate::is_vertex_connected_to_next_only(path)(*path.vertices().begin())); - CHECK(predicate::is_vertex_connected_to_prev_only(path)(path.vertices()[n_source_vertices]) - ); + CHECK(predicate::is_vertex_connected_to_prev_only(path)(path.vertices()[last_vertex_pos])); } SUBCASE("regular_binary_tree(depth) should return a regular binay tree with the given depth") { From d246c92cdb0d329bf77bfc0be21e498e19ab768b Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Sat, 4 Apr 2026 01:40:04 +0200 Subject: [PATCH 6/7] small fixes --- include/gl/util/ranges.hpp | 2 -- tests/include/testing/common/wrnsup.hpp | 20 -------------------- tests/source/gl/test_flat_jagged_vector.cpp | 6 +++--- tests/source/gl/test_flat_matrix.cpp | 6 +++--- tests/source/hgl/test_hypergraph.cpp | 11 ++++++----- 5 files changed, 12 insertions(+), 33 deletions(-) delete mode 100644 tests/include/testing/common/wrnsup.hpp diff --git a/include/gl/util/ranges.hpp b/include/gl/util/ranges.hpp index 9b7d281..99b942c 100644 --- a/include/gl/util/ranges.hpp +++ b/include/gl/util/ranges.hpp @@ -39,8 +39,6 @@ template inline constexpr auto deref_view = std::views::transform([](auto&& p) -> decltype(auto) { return *p; }); -// TODO: add tests - /// @brief A view concatenating two ranges sequentially (C++20 polyfill for C++26 `std::views::concat`). /// /// @warning **GCC 13/14 Bug:** Using branching views (like this or `std::ranges::filter_view`) diff --git a/tests/include/testing/common/wrnsup.hpp b/tests/include/testing/common/wrnsup.hpp deleted file mode 100644 index a89fe9a..0000000 --- a/tests/include/testing/common/wrnsup.hpp +++ /dev/null @@ -1,20 +0,0 @@ -// TODO: delete - -#pragma once - -#if defined(__clang__) || defined(__GNUC__) - -#define SUPPRESS_PRAGMA(x) _Pragma(#x) - -#define SUPPRESS_WARNING_BEGIN(w) \ - SUPPRESS_PRAGMA(GCC diagnostic push) \ - SUPPRESS_PRAGMA(GCC diagnostic ignored w) - -#define SUPPRESS_WARNING_END SUPPRESS_PRAGMA(GCC diagnostic pop) - -#else - -#define SUPPRESS_WARNING_BEGIN(w) -#define SUPPRESS_WARNING_END - -#endif diff --git a/tests/source/gl/test_flat_jagged_vector.cpp b/tests/source/gl/test_flat_jagged_vector.cpp index 482ca3a..c45b0e2 100644 --- a/tests/source/gl/test_flat_jagged_vector.cpp +++ b/tests/source/gl/test_flat_jagged_vector.cpp @@ -1,6 +1,6 @@ #include "doctest.h" -#include "testing/common/wrnsup.hpp" +#include #include #include @@ -92,9 +92,9 @@ TEST_CASE_FIXTURE( sut.push_back({1, 2, 3}); sut.push_back({4, 5}); - SUPPRESS_WARNING_BEGIN("-Wself-move"); + GL_SUPPRESS_WARNING_BEGIN("-Wself-move"); sut = std::move(sut); - SUPPRESS_WARNING_END; + GL_SUPPRESS_WARNING_END; CHECK_EQ(sut.size(), 2uz); CHECK_EQ(sut.data_size(), 5uz); diff --git a/tests/source/gl/test_flat_matrix.cpp b/tests/source/gl/test_flat_matrix.cpp index 3e0a93e..4013cc7 100644 --- a/tests/source/gl/test_flat_matrix.cpp +++ b/tests/source/gl/test_flat_matrix.cpp @@ -1,6 +1,6 @@ #include "doctest.h" -#include "testing/common/wrnsup.hpp" +#include #include #include @@ -98,9 +98,9 @@ TEST_CASE_FIXTURE( sut.push_row({1, 2, 3}); sut.push_row({4, 5, 6}); - SUPPRESS_WARNING_BEGIN("-Wself-move"); + GL_SUPPRESS_WARNING_BEGIN("-Wself-move"); sut = std::move(sut); - SUPPRESS_WARNING_END; + GL_SUPPRESS_WARNING_END; CHECK_EQ(sut.size(), 2uz); CHECK_EQ(sut.n_rows(), 2uz); diff --git a/tests/source/hgl/test_hypergraph.cpp b/tests/source/hgl/test_hypergraph.cpp index ddce5f8..5602fc1 100644 --- a/tests/source/hgl/test_hypergraph.cpp +++ b/tests/source/hgl/test_hypergraph.cpp @@ -1,8 +1,9 @@ #include "doctest.h" -#include "testing/common/wrnsup.hpp" #include "testing/hgl/constants.hpp" #include "testing/hgl/types.hpp" +#include + #include #include #include @@ -524,7 +525,7 @@ TEST_CASE_TEMPLATE_DEFINE( } SUBCASE("incident_hyperedges and degree should throw if the given vertex (id) is invalid") { - SUPPRESS_WARNING_BEGIN("-Warray-bounds"); + GL_SUPPRESS_WARNING_BEGIN("-Warray-bounds"); sut_type sut{constants::n_vertices, constants::n_hyperedges}; CHECK_THROWS_AS( @@ -535,7 +536,7 @@ TEST_CASE_TEMPLATE_DEFINE( static_cast(sut.degree(vertex_type{constants::out_of_rng_vid})), std::out_of_range ); - SUPPRESS_WARNING_END; + GL_SUPPRESS_WARNING_END; } SUBCASE("incident_hyperedges should return an empty view by default and degree should return 0 " @@ -656,7 +657,7 @@ TEST_CASE_TEMPLATE_DEFINE( SUBCASE("incident_vertices and hyperedge_size should throw if the given hyperedge (id) is " "invalid") { - SUPPRESS_WARNING_BEGIN("-Warray-bounds"); + GL_SUPPRESS_WARNING_BEGIN("-Warray-bounds"); sut_type sut{constants::n_vertices, constants::n_hyperedges}; CHECK_THROWS_AS( @@ -668,7 +669,7 @@ TEST_CASE_TEMPLATE_DEFINE( std::out_of_range ); - SUPPRESS_WARNING_END; + GL_SUPPRESS_WARNING_END; } SUBCASE("incident_vertices should return an empty view by default and hyperedge_size should " From 6a65f61b61ae1836e25652cb1ed7177b154d7e9e Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Sat, 4 Apr 2026 01:58:11 +0200 Subject: [PATCH 7/7] resolved comments --- .clangd | 1 + include/gl/graph.hpp | 2 -- include/gl/impl/specialized/adjacency_matrix.hpp | 5 +++-- include/gl/impl/specialized/flat_adjacency_list.hpp | 1 - include/hgl/impl/flat_incidence_list.hpp | 6 ++---- include/hgl/impl/incidence_matrix.hpp | 1 - tests/source/gl/test_graph.cpp | 7 +++++-- 7 files changed, 11 insertions(+), 12 deletions(-) diff --git a/.clangd b/.clangd index 64850f3..0546aea 100644 --- a/.clangd +++ b/.clangd @@ -8,6 +8,7 @@ CompileFlags: - -Wextra - -Wcast-align - -Wconversion + - -Wsign-conversion - -Wunreachable-code - -Wuninitialized - -Wunused diff --git a/include/gl/graph.hpp b/include/gl/graph.hpp index 7ac6c18..2fe58ef 100644 --- a/include/gl/graph.hpp +++ b/include/gl/graph.hpp @@ -10,8 +10,6 @@ #include "gl/io/stream_options_manipulator.hpp" #include "gl/util/ranges.hpp" -#include - #include namespace gl { diff --git a/include/gl/impl/specialized/adjacency_matrix.hpp b/include/gl/impl/specialized/adjacency_matrix.hpp index ec6ebcc..2006ede 100644 --- a/include/gl/impl/specialized/adjacency_matrix.hpp +++ b/include/gl/impl/specialized/adjacency_matrix.hpp @@ -277,9 +277,10 @@ struct undirected_adjacency_matrix { | std::views::filter([](auto edge_id) { return edge_id != invalid_id; }) | std::ranges::to(); - self._matrix.erase(self._matrix.begin() + static_cast(vertex_id)); + const auto vertex_pos = static_cast(vertex_id); + self._matrix.erase(self._matrix.begin() + vertex_pos); for (auto& row : self._matrix) - row.erase(row.begin() + static_cast(vertex_id)); + row.erase(row.begin() + vertex_pos); return removed_edges; } diff --git a/include/gl/impl/specialized/flat_adjacency_list.hpp b/include/gl/impl/specialized/flat_adjacency_list.hpp index b1e41bd..f77f33f 100644 --- a/include/gl/impl/specialized/flat_adjacency_list.hpp +++ b/include/gl/impl/specialized/flat_adjacency_list.hpp @@ -8,7 +8,6 @@ #include "gl/decl/impl_tags.hpp" #include "gl/graph_traits.hpp" #include "gl/impl/specialized/adjacency_list.hpp" -#include "gl/types/core.hpp" #include "gl/types/flat_jagged_vector.hpp" #include diff --git a/include/hgl/impl/flat_incidence_list.hpp b/include/hgl/impl/flat_incidence_list.hpp index bbbc859..7d0367d 100644 --- a/include/hgl/impl/flat_incidence_list.hpp +++ b/include/hgl/impl/flat_incidence_list.hpp @@ -495,12 +495,10 @@ class flat_incidence_list final { const id_type id, const Projection storage_proj ) const noexcept { const auto idx = to_idx(id); - if constexpr (std::same_as) { + if constexpr (std::same_as) return util::concat(this->_tail_storage[idx], this->_head_storage[idx]); - } - else { + else return (this->*storage_proj)[idx]; - } } template diff --git a/include/hgl/impl/incidence_matrix.hpp b/include/hgl/impl/incidence_matrix.hpp index 795b270..abb81fc 100644 --- a/include/hgl/impl/incidence_matrix.hpp +++ b/include/hgl/impl/incidence_matrix.hpp @@ -4,7 +4,6 @@ #pragma once -#include "gl/types/core.hpp" #include "hgl/constants.hpp" #include "hgl/decl/impl_tags.hpp" #include "hgl/directional_tags.hpp" diff --git a/tests/source/gl/test_graph.cpp b/tests/source/gl/test_graph.cpp index 02ef2eb..8bbc742 100644 --- a/tests/source/gl/test_graph.cpp +++ b/tests/source/gl/test_graph.cpp @@ -83,6 +83,8 @@ struct test_graph { validate_full_graph_edges(graph); } + // clang-format off + template GraphType> void validate_full_graph_edges(const GraphType& graph) { REQUIRE(std::ranges::all_of( @@ -90,13 +92,14 @@ struct test_graph { [&graph, expected_n_edges = n_incident_edges_for_fully_connected_vertex(graph)]( const gl::default_id_type vertex_id ) { - return static_cast(gl::util::range_size(graph.adjacent_edges(vertex_id) - )) + return static_cast(gl::util::range_size(graph.adjacent_edges(vertex_id))) == expected_n_edges; } )); } + // clang-format on + template GraphType> gl::size_type n_incident_edges_for_fully_connected_vertex(const GraphType& graph) { return graph.order() - 1uz;