diff --git a/CPP/CMakeLists.txt b/CPP/CMakeLists.txt index bb3625454..71a08a6f5 100644 --- a/CPP/CMakeLists.txt +++ b/CPP/CMakeLists.txt @@ -17,6 +17,7 @@ option(CLIPPER2_HI_PRECISION "Caution: enabling this will compromise performance option(CLIPPER2_UTILS "Build utilities" ON) option(CLIPPER2_EXAMPLES "Build examples" ON) option(CLIPPER2_TESTS "Build tests" ON) +option(CLIPPER2_NO_IOSTREAM "Disable iostream-using header overloads" OFF) option(USE_EXTERNAL_GTEST "Use system-wide installed GoogleTest" OFF) option(USE_EXTERNAL_GBENCHMARK "Use the googlebenchmark" OFF) option(BUILD_SHARED_LIBS "Build shared libs" OFF) @@ -69,6 +70,7 @@ if (NOT (CLIPPER2_USINGZ STREQUAL "ONLY")) Clipper2 PUBLIC CLIPPER2_MAX_DECIMAL_PRECISION=${CLIPPER2_MAX_DECIMAL_PRECISION} $<$:CLIPPER2_HI_PRECISION> + $<$:CLIPPER2_NO_IOSTREAM> ) target_include_directories( @@ -102,6 +104,7 @@ if (NOT (CLIPPER2_USINGZ STREQUAL "OFF")) USINGZ CLIPPER2_MAX_DECIMAL_PRECISION=${CLIPPER2_MAX_DECIMAL_PRECISION} $<$:CLIPPER2_HI_PRECISION> + $<$:CLIPPER2_NO_IOSTREAM> ) target_include_directories( Clipper2Z PUBLIC @@ -130,6 +133,26 @@ set_target_properties(${CLIPPER2_LIBS} PROPERTIES FOLDER Libraries PUBLIC_HEADER "${CLIPPER2_INC}" ) +# Compiles the public headers with CLIPPER2_NO_IOSTREAM defined against +# each shipped library variant. Fails if any iostream usage creeps in +# outside the macro guards. Always-on (independent of CLIPPER2_NO_IOSTREAM +# itself); the targets just inherit each library's other PUBLIC +# definitions (USINGZ, CLIPPER2_HI_PRECISION, ...) via target_link_libraries. +foreach(_lib IN LISTS CLIPPER2_LIBS) + set(_check ${_lib}_no_iostream_check) + add_library(${_check} OBJECT Tests/no_iostream_check.cpp) + target_link_libraries(${_check} PRIVATE ${_lib}) + if(NOT CLIPPER2_NO_IOSTREAM) + target_compile_definitions(${_check} PRIVATE CLIPPER2_NO_IOSTREAM) + endif() + if(CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") + target_compile_options(${_check} PRIVATE /W4 /WX) + elseif(CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU") + target_compile_options(${_check} PRIVATE -Wall -Wextra -Wpedantic -Werror) + endif() + set_target_properties(${_check} PROPERTIES FOLDER Libraries) +endforeach() + if(CLIPPER2_UTILS OR CLIPPER2_TESTS OR CLIPPER2_EXAMPLES) set(CLIPPER2_UTILS_INC Utils/clipper.svg.h diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.core.h b/CPP/Clipper2Lib/include/clipper2/clipper.core.h index 99a52054d..d3af0a08d 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.core.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.core.h @@ -14,7 +14,9 @@ #include #include #include +#ifndef CLIPPER2_NO_IOSTREAM #include +#endif #include #include #include @@ -166,11 +168,13 @@ namespace Clipper2Lib void SetZ(const z_type z_value) { z = z_value; } +#ifndef CLIPPER2_NO_IOSTREAM friend std::ostream& operator<<(std::ostream& os, const Point& point) { os << point.x << "," << point.y << "," << point.z; return os; } +#endif #else @@ -203,11 +207,13 @@ namespace Clipper2Lib return Point(x * scale, y * scale); } +#ifndef CLIPPER2_NO_IOSTREAM friend std::ostream& operator<<(std::ostream& os, const Point& point) { os << point.x << "," << point.y; return os; } +#endif #endif friend bool operator==(const Point& a, const Point& b) @@ -396,10 +402,12 @@ namespace Clipper2Lib return result; } +#ifndef CLIPPER2_NO_IOSTREAM friend std::ostream& operator<<(std::ostream& os, const Rect& rect) { os << "(" << rect.left << "," << rect.top << "," << rect.right << "," << rect.bottom << ") "; return os; } +#endif }; template @@ -498,6 +506,7 @@ namespace Clipper2Lib return Rect(xmin, ymin, xmax, ymax); } +#ifndef CLIPPER2_NO_IOSTREAM template std::ostream& operator << (std::ostream& outstream, const Path& path) { @@ -518,6 +527,7 @@ namespace Clipper2Lib outstream << p; return outstream; } +#endif template diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.h b/CPP/Clipper2Lib/include/clipper2/clipper.h index fe1e299ef..deb1f7bec 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.h @@ -309,6 +309,7 @@ namespace Clipper2Lib { return true; } +#ifndef CLIPPER2_NO_IOSTREAM static void OutlinePolyPath(std::ostream& os, size_t idx, bool isHole, size_t count, const std::string& preamble) { @@ -338,6 +339,7 @@ namespace Clipper2Lib { if (pp.Child(i)->Count()) details::OutlinePolyPathD(os, *pp.Child(i), i, preamble + " "); } +#endif template inline constexpr void MakePathGeneric(const T an_array, @@ -377,6 +379,7 @@ namespace Clipper2Lib { } // end details namespace +#ifndef CLIPPER2_NO_IOSTREAM inline std::ostream& operator<< (std::ostream& os, const PolyTree64& pp) { std::string plural = (pp.Count() == 1) ? " polygon." : " polygons."; @@ -399,6 +402,7 @@ namespace Clipper2Lib { if (!pp.Level()) os << std::endl; return os; } +#endif inline Paths64 PolyTreeToPaths64(const PolyTree64& polytree) { diff --git a/CPP/Tests/no_iostream_check.cpp b/CPP/Tests/no_iostream_check.cpp new file mode 100644 index 000000000..23a8536f5 --- /dev/null +++ b/CPP/Tests/no_iostream_check.cpp @@ -0,0 +1,16 @@ +/******************************************************************************* +* Author : Angus Johnson * +* Date : 3 May 2026 * +* Website : https://www.angusj.com * +* Copyright : Angus Johnson 2010-2026 * +* Purpose : Build-time check that the public headers compile cleanly with * +* CLIPPER2_NO_IOSTREAM defined. * +* License : https://www.boost.org/LICENSE_1_0.txt * +*******************************************************************************/ + +#include "clipper2/clipper.h" + +namespace { +Clipper2Lib::Paths64 _p64; +Clipper2Lib::Rect64 _r64; +} // namespace