diff --git a/src/_backend_agg.h b/src/_backend_agg.h index 36b0dc49c20a..0e7fc918ee42 100644 --- a/src/_backend_agg.h +++ b/src/_backend_agg.h @@ -775,7 +775,7 @@ class span_conv_alpha void prepare() { } - void generate(color_type *span, int x, int y, unsigned len) const + void generate(color_type *span, [[maybe_unused]] int x, [[maybe_unused]] int y, unsigned len) const { do { span->a = (agg::int8u)((double)span->a * m_alpha); @@ -1072,7 +1072,7 @@ class QuadMeshGenerator return 5; } - inline bool should_simplify() + constexpr bool should_simplify() { return false; } diff --git a/src/_c_internal_utils.cpp b/src/_c_internal_utils.cpp index ff9dc4fcc52b..c94c6ecdaf80 100644 --- a/src/_c_internal_utils.cpp +++ b/src/_c_internal_utils.cpp @@ -158,7 +158,7 @@ static py::object mpl_GetCurrentProcessExplicitAppUserModelID(void) { #ifdef _WIN32 - wchar_t* appid = NULL; + wchar_t* appid = nullptr; HRESULT hr = GetCurrentProcessExplicitAppUserModelID(&appid); if (FAILED(hr)) { PyErr_SetFromWindowsErr(hr); diff --git a/src/_image_resample.h b/src/_image_resample.h index 2c48a080a66d..bde62f3895db 100644 --- a/src/_image_resample.h +++ b/src/_image_resample.h @@ -548,7 +548,7 @@ class span_conv_alpha void prepare() {} - void generate(color_type* span, int x, int y, unsigned len) const + void generate(color_type* span, [[maybe_unused]] int x, [[maybe_unused]] int y, unsigned len) const { if (m_alpha != 1.0) { do { diff --git a/src/_path.h b/src/_path.h index 7ee3ba405979..10a9449c01b0 100644 --- a/src/_path.h +++ b/src/_path.h @@ -27,9 +27,7 @@ struct XY double x; double y; - XY() : x(0), y(0) {} - - XY(double x_, double y_) : x(x_), y(y_) + XY(double x_ = 0, double y_ = 0) : x(x_), y(y_) { } @@ -116,7 +114,7 @@ void point_in_path_impl(PointArray &points, PathIterator &path, ResultArray &ins bool all_done; size_t n = safe_first_shape(points); - assert(safe_first_shape(inside_flag) >= n); + assert(static_cast(safe_first_shape(inside_flag)) >= n); std::vector yflag0(n); std::vector subpath_flag(n); @@ -238,13 +236,13 @@ void point_in_path_impl(PointArray &points, PathIterator &path, ResultArray &ins } template -inline void points_in_path(PointArray &points, +constexpr void points_in_path(PointArray &points, const double r, PathIterator &path, agg::trans_affine &trans, ResultArray &result) { - assert(safe_first_shape(result) >= safe_first_shape(points)); + assert(static_cast(safe_first_shape(result)) >= safe_first_shape(points)); for (auto i = 0; i < safe_first_shape(points); ++i) { result[i] = false; } @@ -1021,6 +1019,14 @@ void __add_number(double val, char format_code, int precision, { char *str = PyOS_double_to_string( val, format_code, precision, Py_DTSF_ADD_DOT_0, nullptr); + if (str == nullptr) { + const char* template_msg = "Cannot call PyOS_double_to_string within %s " + "with the following arguments: val=%f, format_code=%c, precision=%d"; + int sz = std::snprintf(nullptr, 0, template_msg, __func__, val, format_code, precision); + std::vector buf(sz + 1); // note +1 for null terminator + std::sprintf(buf.data(), template_msg, __func__, val, format_code, precision); // certain to fit + throw std::invalid_argument(buf.data()); + } // Delete trailing zeros and decimal point char *c = str + strlen(str) - 1; // Start at last character. // Rewind through all the zeros and, if present, the trailing decimal diff --git a/src/_qhull_wrapper.cpp b/src/_qhull_wrapper.cpp index 49632843fc53..0d16c41aa84b 100644 --- a/src/_qhull_wrapper.cpp +++ b/src/_qhull_wrapper.cpp @@ -133,6 +133,11 @@ class QhullInfo { } } + QhullInfo(QhullInfo& other) = delete; + QhullInfo(const QhullInfo& other) = delete; + QhullInfo& operator=(QhullInfo& other) = delete; + QhullInfo& operator=(const QhullInfo& other) = delete; + private: FILE* error_file; qhT* qh; diff --git a/src/_tkagg.cpp b/src/_tkagg.cpp index 955ce2103f90..53aaf3177724 100644 --- a/src/_tkagg.cpp +++ b/src/_tkagg.cpp @@ -46,9 +46,9 @@ using namespace pybind11::literals; * also. */ #define WIN32_DLL -static inline PyObject *PyErr_SetFromWindowsErr(int ierr) { +static constexpr PyObject *PyErr_SetFromWindowsErr(int ierr) { PyErr_SetString(PyExc_OSError, "Call to EnumProcessModules failed"); - return NULL; + return nullptr; } #endif @@ -74,6 +74,8 @@ static_assert(__MINGW64_VERSION_MAJOR >= 6, // Include our own excerpts from the Tcl / Tk headers #include "_tkmini.h" +#include + template static T convert_voidptr(const py::object &obj) @@ -188,6 +190,8 @@ DpiSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, case WM_NCDESTROY: RemoveWindowSubclass(hwnd, DpiSubclassProc, uIdSubclass); break; + default: + assert(1 && "Should never be reached here"); } return DefSubclassProc(hwnd, uMsg, wParam, lParam); diff --git a/src/array.h b/src/array.h index 0e8db3c4cac7..96ea927b2b71 100644 --- a/src/array.h +++ b/src/array.h @@ -22,17 +22,17 @@ class scalar { } - T &operator()(int i, int j = 0, int k = 0) + T &operator()([[maybe_unused]] int i, [[maybe_unused]] int j = 0, [[maybe_unused]] int k = 0) { return m_value; } - const T &operator()(int i, int j = 0, int k = 0) const + const T &operator()([[maybe_unused]] int i, [[maybe_unused]] int j = 0, [[maybe_unused]] int k = 0) const { return m_value; } - int shape(size_t i) + int shape([[maybe_unused]] size_t i) { return 1; } @@ -58,22 +58,22 @@ class empty empty() = default; - T &operator()(int i, int j = 0, int k = 0) + T &operator()([[maybe_unused]] int i, [[maybe_unused]] int j = 0, [[maybe_unused]] int k = 0) { throw std::runtime_error("Accessed empty array"); } - const T &operator()(int i, int j = 0, int k = 0) const + const T &operator()([[maybe_unused]] int i, [[maybe_unused]] int j = 0, [[maybe_unused]] int k = 0) const { throw std::runtime_error("Accessed empty array"); } - sub_t operator[](int i) const + sub_t operator[]([[maybe_unused]] int i) const { return empty(); } - int shape(size_t i) const + int shape([[maybe_unused]] size_t i) const { return 0; } diff --git a/src/ft2font.cpp b/src/ft2font.cpp index 4edb05649021..989186b1d447 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -4,8 +4,10 @@ #include "mplutils.h" #include +#include #include #include +#include #include #include #include @@ -17,8 +19,18 @@ #endif FT2Image::FT2Image(unsigned long width, unsigned long height) - : m_buffer((unsigned char *)calloc(width * height, 1)), m_width(width), m_height(height) + : m_width(width), m_height(height) { + size_t buffer_size = width * height; + if (buffer_size != 0 && buffer_size / width != height) { + const char* template_msg = "Cannot allocate a FT2Image " + "with the following arguments: width=%lu, height=%lu"; + int sz = std::snprintf(nullptr, 0, template_msg, width, height); + std::vector buf(sz + 1); // note +1 for null terminator + std::sprintf(buf.data(), template_msg, width, height); // certain to fit + throw std::overflow_error(buf.data()); + } + m_buffer = static_cast(calloc(width * height, 1)); } FT2Image::~FT2Image() @@ -148,7 +160,10 @@ static FT_Outline_Funcs ft_outline_funcs = { ft_outline_move_to, ft_outline_line_to, ft_outline_conic_to, - ft_outline_cubic_to}; + ft_outline_cubic_to, + 0, + 0, +}; void FT2Font::get_path(std::vector &vertices, std::vector &codes) @@ -169,8 +184,7 @@ FT2Font::get_path(std::vector &vertices, std::vector &cod codes.reserve(estimated_points); if (FT_Error error = FT_Outline_Decompose( &face->glyph->outline, &ft_outline_funcs, &decomposer)) { - throw std::runtime_error("FT_Outline_Decompose failed with error " + - std::to_string(error)); + THROW_FT_ERROR("Decompose font outline", error); } if (vertices.empty()) { // Don't append CLOSEPOLY to null glyphs. return; @@ -474,12 +488,12 @@ void FT2Font::set_text( FT_Error error; error = FT_Load_Glyph(rglyph.ftface, rglyph.index, flags); if (error) { - throw std::runtime_error("failed to load glyph"); + THROW_FT_ERROR("Loading glyphs", error); } FT_Glyph thisGlyph; error = FT_Get_Glyph(rglyph.ftface->glyph, &thisGlyph); if (error) { - throw std::runtime_error("failed to get glyph"); + THROW_FT_ERROR("Getting glyphs", error); } pen.x += rglyph.x_offset; diff --git a/src/ft2font.h b/src/ft2font.h index 383dc0915bbc..4bc4ecbd6c61 100644 --- a/src/ft2font.h +++ b/src/ft2font.h @@ -20,6 +20,7 @@ extern "C" { #include #include FT_BITMAP_H +#include FT_ERRORS_H #include FT_FREETYPE_H #include FT_GLYPH_H #include FT_OUTLINE_H @@ -37,7 +38,7 @@ namespace py = pybind11; #define FIXED_MINOR(val) (unsigned short)(val & 0xffff) // Error handling (error codes are loaded as described in fterror.h). -inline char const* ft_error_string(FT_Error error) { +constexpr char const* ft_error_string(FT_Error error) { #undef __FTERRORS_H__ #define FT_ERROR_START_LIST switch (error) { #define FT_ERRORDEF( e, v, s ) case v: return s; diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index 1a0bd7ddfc0e..140f23578c83 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -1,3 +1,4 @@ +#include #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include #include @@ -301,11 +302,10 @@ class PyFT2Font final : public FT2Font void ft_glyph_warn(FT_ULong charcode, std::set family_names) { - std::set::iterator it = family_names.begin(); - std::stringstream ss; - ss<< (*it ? *it : "unknown family name"); - while(++it != family_names.end()){ - ss<<", "<< (*it ? *it : "unknown family name"); + std::ostringstream ss; + for (const auto& fname : family_names) { + ss << (fname != nullptr ? fname : "unknown family name"); + if (fname != *family_names.rbegin()) ss << ", "; } auto text_helpers = py::module_::import("matplotlib._text_helpers"); @@ -369,15 +369,15 @@ read_from_file_callback(FT_Stream stream, unsigned long offset, unsigned char *b static void close_file_callback(FT_Stream stream) { - PyObject *type, *value, *traceback; - PyErr_Fetch(&type, &value, &traceback); + PyObject* exc = PyErr_GetRaisedException(); PyFT2Font *self = (PyFT2Font *)stream->descriptor.pointer; try { self->py_file.attr("close")(); } catch (py::error_already_set &eas) { eas.discard_as_unraisable(__func__); } - PyErr_Restore(type, value, traceback); + self->py_file = py::object(); + PyErr_SetRaisedException(exc); } const char *PyFT2Font_init__doc__ = R"""( @@ -402,10 +402,9 @@ const char *PyFT2Font_init__doc__ = R"""( This API is private: do not use it directly. )"""; -static PyFT2Font * -PyFT2Font_init(FT_Library ft2Library, py::object filename, - std::optional hinting_factor = std::nullopt, - FT_Long face_index = 0, +static std::unique_ptr +PyFT2Font_init(FT_Library ft2Library, py::object filename, std::optional hinting_factor = std::nullopt, + FT_ULong face_index = 0, std::optional> fallback_list = std::nullopt, std::optional kerning_factor = std::nullopt, bool warn_if_used = false) @@ -423,7 +422,7 @@ PyFT2Font_init(FT_Library ft2Library, py::object filename, kerning_factor = 0; } - if (face_index < 0 || face_index > 0xffff) { + if (face_index > 0xffff) { throw std::range_error("face_index must be between 0 and 65535, inclusive"); } @@ -434,7 +433,7 @@ PyFT2Font_init(FT_Library ft2Library, py::object filename, std::back_inserter(fallback_fonts)); } - auto self = new PyFT2Font(fallback_fonts, warn_if_used); + auto self = std::make_unique(fallback_fonts, warn_if_used); self->set_kerning_factor(*kerning_factor); if (fallback_list) { @@ -452,7 +451,7 @@ PyFT2Font_init(FT_Library ft2Library, py::object filename, // file we opened, and nullptr for a caller-owned one. auto stream_font_via_python = [&](FT_Stream_CloseFunc close) { self->stream.size = 0x7fffffff; // Unknown size. - self->stream.descriptor.pointer = self; + self->stream.descriptor.pointer = self.get(); self->stream.read = &read_from_file_callback; self->stream.close = close; open_args.flags = FT_OPEN_STREAM; @@ -1603,10 +1602,10 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) [ft2Library]( py::object filename, std::optional hinting_factor = std::nullopt, - FT_Long face_index = 0, + FT_ULong face_index = 0, std::optional> fallback_list = std::nullopt, std::optional kerning_factor = std::nullopt, - bool warn_if_used = false) -> PyFT2Font * + bool warn_if_used = false) -> std::unique_ptr { return PyFT2Font_init(ft2Library, filename, hinting_factor, face_index, fallback_list, kerning_factor, warn_if_used); diff --git a/src/mplutils.h b/src/mplutils.h index 2a7d63c871f5..da4c28196683 100644 --- a/src/mplutils.h +++ b/src/mplutils.h @@ -28,12 +28,12 @@ #endif -inline int mpl_round_to_int(double v) +constexpr int mpl_round_to_int(double v) { return (int)(v + ((v >= 0.0) ? 0.5 : -0.5)); } -inline double mpl_round(double v) +constexpr double mpl_round(double v) { return (double)mpl_round_to_int(v); } @@ -64,7 +64,7 @@ template overloaded(Ts...) -> overloaded; // Check that array has shape (N, d1) or (N, d1, d2). We cast d1, d2 to longs // so that we don't need to access the NPY_INTP_FMT macro here. template -inline void check_trailing_shape(T array, char const* name, long d1) +constexpr void check_trailing_shape(T array, char const* name, long d1) { if (array.ndim() != 2) { throw py::value_error( @@ -83,7 +83,7 @@ inline void check_trailing_shape(T array, char const* name, long d1) } template -inline void check_trailing_shape(T array, char const* name, long d1, long d2) +constexpr void check_trailing_shape(T array, char const* name, long d1, long d2) { if (array.ndim() != 3) { throw py::value_error( diff --git a/src/path_converters.h b/src/path_converters.h index 2a1f4ca6fe70..65f5b5f4aa33 100644 --- a/src/path_converters.h +++ b/src/path_converters.h @@ -5,6 +5,7 @@ #include +#include #include #include #include @@ -48,7 +49,7 @@ output. It is designed to be as fast as possible vs. the STL's queue which is more flexible. */ -template +template class EmbeddedQueue { protected: @@ -59,14 +60,6 @@ class EmbeddedQueue struct item { - item() = default; - - inline void set(const unsigned cmd_, const double x_, const double y_) - { - cmd = cmd_; - x = x_; - y = y_; - } unsigned cmd; double x; double y; @@ -75,17 +68,17 @@ class EmbeddedQueue int m_queue_write; item m_queue[QueueSize]; - inline void queue_push(const unsigned cmd, const double x, const double y) + constexpr void queue_push(const unsigned cmd, const double x, const double y) { - m_queue[m_queue_write++].set(cmd, x, y); + m_queue[m_queue_write++] = item{cmd, x, y}; } - inline bool queue_nonempty() + constexpr bool queue_nonempty() { return m_queue_read < m_queue_write; } - inline bool queue_pop(unsigned *cmd, double *x, double *y) + constexpr bool queue_pop(unsigned *cmd, double *x, double *y) { if (queue_nonempty()) { const item &front = m_queue[m_queue_read++]; @@ -102,7 +95,7 @@ class EmbeddedQueue return false; } - inline void queue_clear() + constexpr void queue_clear() { m_queue_read = 0; m_queue_write = 0; @@ -123,7 +116,7 @@ static const size_t num_extra_points_map[] = that matter, like crypto. We are implementing this ourselves rather than using the C stdlib so that the seed state is not shared with other third-party code. There are recent C++ options, but we - still require nothing later than C++98 for compatibility + still require nothing later than C++17 for compatibility reasons. */ class RandomNumberGenerator { @@ -138,9 +131,9 @@ class RandomNumberGenerator public: RandomNumberGenerator() : m_seed(0) {} - RandomNumberGenerator(int seed) : m_seed(seed) {} + RandomNumberGenerator(uint32_t seed) : m_seed(seed) {} - void seed(int seed) + void seed(uint32_t seed) { m_seed = seed; } @@ -174,26 +167,28 @@ class PathNanRemover : protected EmbeddedQueue<4> /* has_codes should be true if the path contains bezier curve segments, or * closed loops, as this requires a slower algorithm to remove the NaNs. * When in doubt, set to true. + * + * Also ignore all close/end_poly commands until after the first valid + * (nan-free) command is encountered */ PathNanRemover(VertexSource &source, bool remove_nans, bool has_codes) : m_source(&source), m_remove_nans(remove_nans), m_has_codes(has_codes), + valid_segment_exists(false), m_last_segment_valid(false), m_was_broken(false), m_initX(nan("")), m_initY(nan("")) { - // ignore all close/end_poly commands until after the first valid - // (nan-free) command is encountered - valid_segment_exists = false; + } - inline void rewind(unsigned path_id) + constexpr void rewind(unsigned path_id) { queue_clear(); m_source->rewind(path_id); } - inline unsigned vertex(double *x, double *y) + constexpr unsigned vertex(double *x, double *y) { - unsigned code; + unsigned code = 0; if (!m_remove_nans) { return m_source->vertex(x, y); @@ -373,7 +368,7 @@ class PathClipper : public EmbeddedQueue<3> m_cliprect.y2 += 1.0; } - inline void rewind(unsigned path_id) + constexpr void rewind(unsigned path_id) { m_has_init = false; m_was_clipped = false; @@ -598,7 +593,11 @@ class PathSnapper x_start = x1; y_start = y1; break; + default: + // Should never happen + assert(1 && "Should never be reached here"); } + x0 = x1; y0 = y1; } @@ -636,14 +635,14 @@ class PathSnapper source.rewind(0); } - inline void rewind(unsigned path_id) + constexpr void rewind(unsigned path_id) { m_source->rewind(path_id); } - inline unsigned vertex(double *x, double *y) + constexpr unsigned vertex(double *x, double *y) { - unsigned code; + unsigned code = 0; code = m_source->vertex(x, y); if (m_snap && agg::is_vertex(code)) { *x = floor(*x + 0.5) + m_snap_value; @@ -652,7 +651,7 @@ class PathSnapper return code; } - inline bool is_snapping() + constexpr bool is_snapping() { return m_snap; } @@ -725,7 +724,7 @@ class PathSimplifier : protected EmbeddedQueue<9> // empty } - inline void rewind(unsigned path_id) + constexpr void rewind(unsigned path_id) { queue_clear(); m_moveto = true; @@ -1000,7 +999,7 @@ class PathSimplifier : protected EmbeddedQueue<9> double m_currVecStartX; double m_currVecStartY; - inline void _push(double *x, double *y) + constexpr void _push(double *x, double *y) { bool needToPushBack = (m_dnorm2BackwardMax > 0.0); @@ -1151,7 +1150,7 @@ class Sketch return code; } - inline void rewind(unsigned path_id) + constexpr void rewind(unsigned path_id) { m_has_last = false; m_p = 0.0; diff --git a/src/py_adaptors.h b/src/py_adaptors.h index 8c304dca43e7..925e2653ebea 100644 --- a/src/py_adaptors.h +++ b/src/py_adaptors.h @@ -78,8 +78,21 @@ class PathIterator m_simplify_threshold = other.m_simplify_threshold; } + inline PathIterator& operator=(const PathIterator& other) { + if (this == &other) return *this; + m_vertices = other.m_vertices; + m_codes = other.m_codes; + + m_iterator = 0; + m_total_vertices = other.m_total_vertices; + + m_should_simplify = other.m_should_simplify; + m_simplify_threshold = other.m_simplify_threshold; + return *this; + } + inline void - set(py::object vertices, py::object codes, bool should_simplify, double simplify_threshold) + set(py::object vertices, py::object codes, bool should_simplify = false, double simplify_threshold = 0.0) { m_should_simplify = should_simplify; m_simplify_threshold = simplify_threshold; @@ -101,11 +114,6 @@ class PathIterator m_iterator = 0; } - inline void set(py::object vertices, py::object codes) - { - set(vertices, codes, false, 0.0); - } - inline unsigned vertex(double *x, double *y) { if (m_iterator >= m_total_vertices) { @@ -126,7 +134,7 @@ class PathIterator } } - inline void rewind(unsigned path_id) + constexpr void rewind(unsigned path_id) { m_iterator = path_id; } @@ -136,7 +144,7 @@ class PathIterator return m_total_vertices; } - inline bool should_simplify() const + constexpr bool should_simplify() const { return m_should_simplify; }