diff --git a/doc/modules/ROOT/pages/examples.adoc b/doc/modules/ROOT/pages/examples.adoc index 6973ea2..303a3f1 100644 --- a/doc/modules/ROOT/pages/examples.adoc +++ b/doc/modules/ROOT/pages/examples.adoc @@ -459,6 +459,47 @@ verified_u32 NE round-trip: 0xcafebabe ---- ==== +[#examples_std_numeric_usage] +== Standard Library Compatibility + +Safe integer types work seamlessly with standard library algorithms that require only comparison operators, such as `std::min`, `std::max`, `std::clamp`, `std::sort`, and `std::minmax_element`. + +NOTE: `std::gcd`, `std::lcm`, and `std::midpoint` do *not* work because they `static_assert` on `std::is_integral`, which is `false` for class types. + +.This https://github.com/boostorg/safe_numbers/blob/develop/examples/std_numeric_usage.cpp[example] demonstrates standard library algorithm compatibility. +==== +[source, c++] +---- +include::example$std_numeric_usage.cpp[] +---- + +Output: +---- +=== std::min / std::max === +std::min(42, 100) = 42 +std::max(42, 100) = 100 +std::min({30, 10, 50}) = 10 +std::max({30, 10, 50}) = 50 + +=== std::clamp === +std::clamp(5, 10, 200) = 10 +std::clamp(50, 10, 200) = 50 +std::clamp(999, 10, 200) = 200 + +=== std::sort === +sorted: 10 20 30 40 50 + +=== std::minmax_element === +min element = 10 +max element = 50 + +=== Other widths === +std::min(u8(10), u8(20)) = 10 +std::max(u16(100), u16(200)) = 200 +std::clamp(u64(500), u64(0), u64(100)) = 100 +---- +==== + [#examples_verified_construction] == Verified Types: Construction and Runtime Usage diff --git a/examples/std_numeric_usage.cpp b/examples/std_numeric_usage.cpp new file mode 100644 index 0000000..1a39320 --- /dev/null +++ b/examples/std_numeric_usage.cpp @@ -0,0 +1,83 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +// +// This example demonstrates that standard library algorithms requiring +// only comparison operators work seamlessly with safe integer types. +// +// Note: std::gcd, std::lcm, and std::midpoint do NOT work because they +// static_assert on std::is_integral, which is false for class types. +// + +#include +#include +#include +#include +#include + +int main() +{ + using namespace boost::safe_numbers; + + // ---- std::min / std::max ---- + std::cout << "=== std::min / std::max ===\n"; + { + const auto a = u32{42U}; + const auto b = u32{100U}; + std::cout << "std::min(42, 100) = " << std::min(a, b) << '\n'; + std::cout << "std::max(42, 100) = " << std::max(a, b) << '\n'; + + // Also works with initializer lists + std::cout << "std::min({30, 10, 50}) = " + << std::min({u32{30U}, u32{10U}, u32{50U}}) << '\n'; + std::cout << "std::max({30, 10, 50}) = " + << std::max({u32{30U}, u32{10U}, u32{50U}}) << '\n'; + } + + // ---- std::clamp ---- + std::cout << "\n=== std::clamp ===\n"; + { + const auto lo = u32{10U}; + const auto hi = u32{200U}; + std::cout << "std::clamp(5, 10, 200) = " << std::clamp(u32{5U}, lo, hi) << '\n'; + std::cout << "std::clamp(50, 10, 200) = " << std::clamp(u32{50U}, lo, hi) << '\n'; + std::cout << "std::clamp(999, 10, 200) = " << std::clamp(u32{999U}, lo, hi) << '\n'; + } + + // ---- std::sort ---- + std::cout << "\n=== std::sort ===\n"; + { + std::array values {u32{50U}, u32{10U}, u32{40U}, u32{20U}, u32{30U}}; + std::sort(values.begin(), values.end()); + std::cout << "sorted: "; + for (const auto& v : values) + { + std::cout << v << ' '; + } + std::cout << '\n'; + } + + // ---- std::minmax_element ---- + std::cout << "\n=== std::minmax_element ===\n"; + { + const std::array values {u32{50U}, u32{10U}, u32{40U}, u32{20U}, u32{30U}}; + const auto [min_it, max_it] = std::minmax_element(values.begin(), values.end()); + std::cout << "min element = " << *min_it << '\n'; + std::cout << "max element = " << *max_it << '\n'; + } + + // ---- Works with different widths ---- + std::cout << "\n=== Other widths ===\n"; + { + std::cout << "std::min(u8(10), u8(20)) = " + << static_cast(std::min(u8{10}, u8{20})) << '\n'; + std::cout << "std::max(u16(100), u16(200)) = " + << std::max(u16{static_cast(100)}, + u16{static_cast(200)}) << '\n'; + std::cout << "std::clamp(u64(500), u64(0), u64(100)) = " + << std::clamp(u64{500ULL}, u64{0ULL}, u64{100ULL}) << '\n'; + } + + return 0; +} diff --git a/test/Jamfile b/test/Jamfile index 2c05578..6327bb7 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -181,3 +181,4 @@ run ../examples/verified_mixed_ops.cpp ; compile-fail ../examples/compile_fail_verified_overflow.cpp ; run ../examples/safe_numerics_comparison.cpp ; run ../examples/byte_conversions.cpp ; +run ../examples/std_numeric_usage.cpp ;