diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index fea4d208113..a918c1db316 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -696,7 +696,13 @@ public: halfedge_descriptor halfedge; int count; }; - CGAL::unordered_flat_map points_to_halfedge; + + using Point_to_point_info_map = std::conditional_t< + CGAL::is_hashable_v, + CGAL::unordered_flat_map, + std::map>; + + Point_to_point_info_map points_to_halfedge; auto visited_halfedges = get(CGAL::dynamic_halfedge_property_t(), mesh); for(const auto h : halfedges(mesh)) { diff --git a/Kernel_23/include/CGAL/Kernel/hash_functions.h b/Kernel_23/include/CGAL/Kernel/hash_functions.h index a68197941d8..224483c492b 100644 --- a/Kernel_23/include/CGAL/Kernel/hash_functions.h +++ b/Kernel_23/include/CGAL/Kernel/hash_functions.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019 +// Copyright (c) 2019,2025 // GeometryFactory (France) // // This file is part of CGAL (www.cgal.org) @@ -9,6 +9,8 @@ // // // Author(s) : Simon Giraudot +// +// Test file: test/Kernel_23/test_hash_functions.cpp #ifndef CGAL_KERNEL_HASH_FUNCTIONS_H #define CGAL_KERNEL_HASH_FUNCTIONS_H @@ -16,20 +18,73 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + namespace CGAL { using boost::hash_value; +template +inline constexpr bool has_rep_tag_v = false; + template -inline std::enable_if_t::value, std::size_t> +inline constexpr bool has_rep_tag_v> = true; + +template +struct Rep_tag { + using type = void; +}; + +template +struct Rep_tag>> { + using type = typename K::Rep_tag; +}; + +template +using Rep_tag_t = typename Rep_tag::type; + +template +inline constexpr bool is_Cartesian_v = std::is_same, Cartesian_tag>::value; + +template +struct Is_kernel_hashable : public std::false_type {}; + +template +struct Is_kernel_hashable()))>> : public std::true_type {}; + +template +inline constexpr bool is_kernel_hashable_v = Is_kernel_hashable::value; + +template +using enable_if_Cartesian_and_hashable_t = + std::enable_if_t && is_kernel_hashable_v, T>; + +template +inline enable_if_Cartesian_and_hashable_t hash_value (const Aff_transformation_2& transform) { std::size_t result = hash_value(transform.cartesian(0,0)); for(int i=0; i < 3; ++i) - for(int j = 0; j < 3; ++j) - if (!(i == 0 && j == 0)) - boost::hash_combine(result, hash_value(transform.cartesian(i,j))); + for(int j = (i == 0 ? 1 : 0); j < 3; ++j) + boost::hash_combine(result, hash_value(transform.cartesian(i,j))); return result; } @@ -44,7 +99,7 @@ hash_value (const Bbox_2& bbox) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Circle_2& circle) { std::size_t result = hash_value(circle.center()); @@ -54,7 +109,7 @@ hash_value (const Circle_2& circle) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Iso_rectangle_2& iso_rectangle) { std::size_t result = hash_value((iso_rectangle.min)()); @@ -63,7 +118,7 @@ hash_value (const Iso_rectangle_2& iso_rectangle) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Point_2& point) { std::size_t result = hash_value(point.x()); @@ -72,7 +127,7 @@ hash_value (const Point_2& point) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Segment_2& segment) { std::size_t result = hash_value(segment.source()); @@ -81,7 +136,7 @@ hash_value (const Segment_2& segment) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Vector_2& vector) { std::size_t result = hash_value(vector.x()); @@ -90,7 +145,7 @@ hash_value (const Vector_2& vector) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Weighted_point_2& weighed_point) { std::size_t result = hash_value(weighed_point.point()); @@ -99,14 +154,13 @@ hash_value (const Weighted_point_2& weighed_point) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Aff_transformation_3& transform) { std::size_t result = hash_value(transform.cartesian(0,0)); for(int i = 0; i < 3; ++i) - for(int j = 0; j < 4; ++j) - if (!(i == 0 && j == 0)) - boost::hash_combine(result, hash_value(transform.cartesian(i,j))); + for(int j = (i == 0 ? 1 : 0); j < 4; ++j) + boost::hash_combine(result, hash_value(transform.cartesian(i,j))); return result; } @@ -123,7 +177,7 @@ hash_value (const Bbox_3& bbox) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Iso_cuboid_3& iso_cuboid) { std::size_t result = hash_value((iso_cuboid.min)()); @@ -132,7 +186,7 @@ hash_value (const Iso_cuboid_3& iso_cuboid) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Point_3& point) { std::size_t result = hash_value(point.x()); @@ -142,7 +196,7 @@ hash_value (const Point_3& point) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Segment_3& segment) { std::size_t result = hash_value(segment.source()); @@ -151,7 +205,7 @@ hash_value (const Segment_3& segment) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Sphere_3& sphere) { std::size_t result = hash_value(sphere.center()); @@ -161,7 +215,7 @@ hash_value (const Sphere_3& sphere) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Vector_3& vector) { std::size_t result = hash_value(vector.x()); @@ -171,7 +225,7 @@ hash_value (const Vector_3& vector) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Weighted_point_3& weighed_point) { std::size_t result = hash_value(weighed_point.point()); @@ -179,93 +233,46 @@ hash_value (const Weighted_point_3& weighed_point) return result; } +struct Forward_to_hash_value { + template + std::size_t operator()(T&& t) const { + using boost::hash_value; + return hash_value(std::forward(t)); + } +}; + +template +struct Maybe_forward_to_hash_value { + Maybe_forward_to_hash_value() = delete; + Maybe_forward_to_hash_value(const Maybe_forward_to_hash_value&) = delete; +}; + +template +struct Maybe_forward_to_hash_value>> + : public Forward_to_hash_value {}; + + } //namespace CGAL // overloads of std::hash used for using std::unordered_[set/map] on CGAL Kernel objects -namespace std -{ +namespace std { +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template <> struct hash : CGAL::Forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template <> struct hash : CGAL::Forward_to_hash_value {}; +} // namespace std -template struct hash > { - std::size_t operator() (const CGAL::Aff_transformation_2& transform) const { - return CGAL::hash_value (transform); - } -}; -template <> struct hash { - std::size_t operator() (const CGAL::Bbox_2& bbox) const { - return CGAL::hash_value (bbox); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Circle_2& circle) const { - return CGAL::hash_value (circle); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Iso_rectangle_2& iso_rectangle) const { - return CGAL::hash_value (iso_rectangle); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Point_2& point) const { - return CGAL::hash_value (point); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Segment_2& segment) const { - return CGAL::hash_value (segment); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Vector_2& vector) const { - return CGAL::hash_value (vector); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Weighted_point_2& weighted_point) const { - return CGAL::hash_value (weighted_point); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Aff_transformation_3& transform) const { - return CGAL::hash_value (transform); - } -}; -template <> struct hash { - std::size_t operator() (const CGAL::Bbox_3& bbox) const { - return CGAL::hash_value (bbox); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Iso_cuboid_3& iso_cuboid) const { - return CGAL::hash_value (iso_cuboid); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Point_3& point) const { - return CGAL::hash_value (point); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Segment_3& segment) const { - return CGAL::hash_value (segment); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Sphere_3& sphere) const { - return CGAL::hash_value (sphere); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Vector_3& vector) const { - return CGAL::hash_value (vector); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Weighted_point_3& weighted_point) const { - return CGAL::hash_value (weighted_point); - } -}; - -} #endif // CGAL_KERNEL_HASH_FUNCTIONS_H diff --git a/Kernel_23/test/Kernel_23/test_hash_functions.cpp b/Kernel_23/test/Kernel_23/test_hash_functions.cpp index 43a5cb8ead6..4ccf96acdb0 100644 --- a/Kernel_23/test/Kernel_23/test_hash_functions.cpp +++ b/Kernel_23/test/Kernel_23/test_hash_functions.cpp @@ -1,13 +1,18 @@ +// test partially generated by Github Copilot + #include #include #include - #include +#include typedef CGAL::Simple_cartesian SC; typedef CGAL::Exact_predicates_inexact_constructions_kernel Epick; +static_assert(CGAL::is_kernel_hashable_v); +static_assert(CGAL::is_kernel_hashable_v); + template void test (const Object& obj) { @@ -67,5 +72,3 @@ int main() return 0; } - - diff --git a/STL_Extension/include/CGAL/unordered_flat_map.h b/STL_Extension/include/CGAL/unordered_flat_map.h index 5354e7d5db3..af55f9975bf 100644 --- a/STL_Extension/include/CGAL/unordered_flat_map.h +++ b/STL_Extension/include/CGAL/unordered_flat_map.h @@ -38,6 +38,22 @@ namespace CGAL { + namespace internal_is_hashable { + using boost::hash_value; + + template + struct Has_hash_value : std::false_type {}; + + template + struct Has_hash_value()))>> + : std::is_convertible())), std::size_t> + {}; + } + + template + inline constexpr bool is_hashable_v = + internal_is_hashable::Has_hash_value::value && std::is_default_constructible_v>; + template < typename Key, typename T, diff --git a/STL_Extension/test/STL_Extension/CMakeLists.txt b/STL_Extension/test/STL_Extension/CMakeLists.txt index b1007acfcb5..0639a015ca4 100644 --- a/STL_Extension/test/STL_Extension/CMakeLists.txt +++ b/STL_Extension/test/STL_Extension/CMakeLists.txt @@ -28,6 +28,7 @@ create_single_source_cgal_program("test_dispatch_output.cpp") create_single_source_cgal_program("test_Flattening_iterator.cpp") create_single_source_cgal_program("test_Handle_with_policy.cpp") create_single_source_cgal_program("test_In_place_list.cpp") +create_single_source_cgal_program("test_is_hashable.cpp") create_single_source_cgal_program("test_is_iterator.cpp") create_single_source_cgal_program("test_is_streamable.cpp") create_single_source_cgal_program("test_lexcompare_outputrange.cpp") diff --git a/STL_Extension/test/STL_Extension/test_is_hashable.cpp b/STL_Extension/test/STL_Extension/test_is_hashable.cpp new file mode 100644 index 00000000000..043d5839e4e --- /dev/null +++ b/STL_Extension/test/STL_Extension/test_is_hashable.cpp @@ -0,0 +1,86 @@ +// test partially generated by Github Copilot + +#include +#include +#include +#include +#include +#include +#include +#include +#include // std::pair + +// Test types without hash support +struct No_hash {}; +struct No_default_constructible_hash {}; + +// Provide boost::hash_value for No_default_constructible_hash +namespace boost { + std::size_t hash_value(const No_default_constructible_hash&) { return 42; } +} + +// But make std::hash not default constructible +namespace std { + template <> + struct hash { + hash() = delete; // Not default constructible + std::size_t operator()(const No_default_constructible_hash&) const { return 42; } + }; +} + +int main() { + using CGAL::is_hashable_v; + using SC = CGAL::Simple_cartesian; + using Epick = CGAL::Exact_predicates_inexact_constructions_kernel; + using Epeck = CGAL::Exact_predicates_exact_constructions_kernel; + + // Built-in types should be hashable + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + + // Types without hash support should not be hashable + static_assert(!is_hashable_v); + + // Types with hash_value but non-default-constructible std::hash should not be hashable + static_assert(!is_hashable_v); + + // CGAL kernel objects should be hashable (with Cartesian kernels) + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + + // Test with Epick kernel + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + + // Test with Epeck kernel + static_assert(!is_hashable_v); + static_assert(!is_hashable_v); + static_assert(!is_hashable_v); + static_assert(!is_hashable_v); + + // Bbox types should be hashable + static_assert(is_hashable_v); + static_assert(is_hashable_v); + + // std::pair should not be hashable (no std::hash specialization in standard) + static_assert(!is_hashable_v>); + + return 0; +}