diff --git a/src/_backend_agg_wrapper.cpp b/src/_backend_agg_wrapper.cpp index 3dd50b31f64a..1230f8869f5d 100644 --- a/src/_backend_agg_wrapper.cpp +++ b/src/_backend_agg_wrapper.cpp @@ -63,31 +63,30 @@ PyRendererAgg_draw_text_image(RendererAgg *self, double angle, GCAgg &gc) { - int x, y; - - if (auto value = std::get_if(&vx)) { - auto api = py::module_::import("matplotlib._api"); - auto warn = api.attr("warn_deprecated"); - warn("since"_a="3.10", "name"_a="x", "obj_type"_a="parameter as float", - "alternative"_a="int(x)"); - x = static_cast(*value); - } else if (auto value = std::get_if(&vx)) { - x = *value; - } else { - throw std::runtime_error("Should not happen"); - } - - if (auto value = std::get_if(&vy)) { - auto api = py::module_::import("matplotlib._api"); - auto warn = api.attr("warn_deprecated"); - warn("since"_a="3.10", "name"_a="y", "obj_type"_a="parameter as float", - "alternative"_a="int(y)"); - y = static_cast(*value); - } else if (auto value = std::get_if(&vy)) { - y = *value; - } else { - throw std::runtime_error("Should not happen"); - } + int x = std::visit(overloaded { + [&](double value) { + auto api = py::module_::import("matplotlib._api"); + auto warn = api.attr("warn_deprecated"); + warn("since"_a="3.10", "name"_a="x", "obj_type"_a="parameter as float", + "alternative"_a="int(x)"); + return static_cast(value); + }, + [](int value) { + return value; + } + }, vx); + int y = std::visit(overloaded { + [&](double value) { + auto api = py::module_::import("matplotlib._api"); + auto warn = api.attr("warn_deprecated"); + warn("since"_a="3.10", "name"_a="y", "obj_type"_a="parameter as float", + "alternative"_a="int(x)"); + return static_cast(value); + }, + [](int value) { + return value; + } + }, vy); // TODO: This really shouldn't be mutable, but Agg's renderer buffers aren't const. auto image = image_obj.mutable_unchecked<2>(); diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index a348f0d312b6..132daa30d555 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -4,6 +4,7 @@ #include #include "ft2font.h" +#include "mplutils.h" #include "_enums.h" #include @@ -18,23 +19,20 @@ using double_or_ = std::variant; template static T -_double_to_(const char *name, double_or_ &var) +_double_to_(const char *name, double_or_ var) { - if (auto value = std::get_if(&var)) { - auto api = py::module_::import("matplotlib._api"); - auto warn = api.attr("warn_deprecated"); - warn("since"_a="3.10", "name"_a=name, "obj_type"_a="parameter as float", - "alternative"_a="int({})"_s.format(name)); - return static_cast(*value); - } else if (auto value = std::get_if(&var)) { - return *value; - } else { - // pybind11 will have only allowed types that match the variant, so this `else` - // can't happen. We only have this case because older macOS doesn't support - // `std::get` and using the conditional `std::get_if` means an `else` to silence - // compiler warnings about "unhandled" cases. - throw std::runtime_error("Should not happen"); - } + return std::visit(overloaded { + [&](double value) { + auto api = py::module_::import("matplotlib._api"); + auto warn = api.attr("warn_deprecated"); + warn("since"_a="3.10", "name"_a=name, "obj_type"_a="parameter as float", + "alternative"_a="int({})"_s.format(name)); + return static_cast(value); + }, + [](T value) { + return value; + } + }, var); } /********************************************************************** @@ -603,22 +601,18 @@ static int PyFT2Font_get_kerning(PyFT2Font *self, FT_UInt left, FT_UInt right, std::variant mode_or_int) { - FT_Kerning_Mode mode; - - if (auto value = std::get_if(&mode_or_int)) { - auto api = py::module_::import("matplotlib._api"); - auto warn = api.attr("warn_deprecated"); - warn("since"_a="3.10", "name"_a="mode", "obj_type"_a="parameter as int", - "alternative"_a="Kerning enum values"); - mode = static_cast(*value); - } else if (auto value = std::get_if(&mode_or_int)) { - mode = *value; - } else { - // NOTE: this can never happen as pybind11 would have checked the type in the - // Python wrapper before calling this function, but we need to keep the - // std::get_if instead of std::get for macOS 10.12 compatibility. - throw py::type_error("mode must be Kerning or int"); - } + FT_Kerning_Mode mode = std::visit(overloaded { + [&](FT_UInt value) { + auto api = py::module_::import("matplotlib._api"); + auto warn = api.attr("warn_deprecated"); + warn("since"_a="3.10", "name"_a="mode", "obj_type"_a="parameter as int", + "alternative"_a="Kerning enum values"); + return static_cast(value); + }, + [](FT_Kerning_Mode value) { + return value; + } + }, mode_or_int); return self->get_kerning(left, right, mode); } @@ -708,36 +702,28 @@ PyFT2Font_set_text(PyFT2Font *self, std::u32string_view text, double angle = 0.0 std::variant languages_or_str = nullptr) { std::vector xys; - LoadFlags flags; - - if (auto value = std::get_if(&flags_or_int)) { - auto api = py::module_::import("matplotlib._api"); - auto warn = api.attr("warn_deprecated"); - warn("since"_a="3.10", "name"_a="flags", "obj_type"_a="parameter as int", - "alternative"_a="LoadFlags enum values"); - flags = static_cast(*value); - } else if (auto value = std::get_if(&flags_or_int)) { - flags = *value; - } else { - // NOTE: this can never happen as pybind11 would have checked the type in the - // Python wrapper before calling this function, but we need to keep the - // std::get_if instead of std::get for macOS 10.12 compatibility. - throw py::type_error("flags must be LoadFlags or int"); - } - - FT2Font::LanguageType languages; - if (auto value = std::get_if(&languages_or_str)) { - languages = std::move(*value); - } else if (auto value = std::get_if(&languages_or_str)) { - languages = std::vector{ - FT2Font::LanguageRange{*value, 0, text.size()} - }; - } else { - // NOTE: this can never happen as pybind11 would have checked the type in the - // Python wrapper before calling this function, but we need to keep the - // std::get_if instead of std::get for macOS 10.12 compatibility. - throw py::type_error("languages must be str or list of tuple"); - } + LoadFlags flags = std::visit(overloaded { + [&](FT_Int32 value) { + auto api = py::module_::import("matplotlib._api"); + auto warn = api.attr("warn_deprecated"); + warn("since"_a="3.10", "name"_a="flags", "obj_type"_a="parameter as int", + "alternative"_a="LoadFlags enum values"); + return static_cast(value); + }, + [](LoadFlags value) { + return value; + } + }, flags_or_int); + + FT2Font::LanguageType languages = std::visit(overloaded { + [](FT2Font::LanguageType languages) { + return languages; + }, + [&](std::string value) { + return FT2Font::LanguageType{{ + FT2Font::LanguageRange{value, 0, text.size()}}}; + } + }, languages_or_str); self->set_text(text, angle, static_cast(flags), features, languages, xys); @@ -783,22 +769,18 @@ PyFT2Font_load_char(PyFT2Font *self, long charcode, { bool fallback = true; FT2Font *ft_object = nullptr; - LoadFlags flags; - - if (auto value = std::get_if(&flags_or_int)) { - auto api = py::module_::import("matplotlib._api"); - auto warn = api.attr("warn_deprecated"); - warn("since"_a="3.10", "name"_a="flags", "obj_type"_a="parameter as int", - "alternative"_a="LoadFlags enum values"); - flags = static_cast(*value); - } else if (auto value = std::get_if(&flags_or_int)) { - flags = *value; - } else { - // NOTE: this can never happen as pybind11 would have checked the type in the - // Python wrapper before calling this function, but we need to keep the - // std::get_if instead of std::get for macOS 10.12 compatibility. - throw py::type_error("flags must be LoadFlags or int"); - } + LoadFlags flags = std::visit(overloaded { + [&](FT_Int32 value) { + auto api = py::module_::import("matplotlib._api"); + auto warn = api.attr("warn_deprecated"); + warn("since"_a="3.10", "name"_a="flags", "obj_type"_a="parameter as int", + "alternative"_a="LoadFlags enum values"); + return static_cast(value); + }, + [](LoadFlags value) { + return value; + } + }, flags_or_int); self->load_char(charcode, static_cast(flags), ft_object, fallback); @@ -835,22 +817,18 @@ static PyGlyph * PyFT2Font_load_glyph(PyFT2Font *self, FT_UInt glyph_index, std::variant flags_or_int = LoadFlags::FORCE_AUTOHINT) { - LoadFlags flags; - - if (auto value = std::get_if(&flags_or_int)) { - auto api = py::module_::import("matplotlib._api"); - auto warn = api.attr("warn_deprecated"); - warn("since"_a="3.10", "name"_a="flags", "obj_type"_a="parameter as int", - "alternative"_a="LoadFlags enum values"); - flags = static_cast(*value); - } else if (auto value = std::get_if(&flags_or_int)) { - flags = *value; - } else { - // NOTE: this can never happen as pybind11 would have checked the type in the - // Python wrapper before calling this function, but we need to keep the - // std::get_if instead of std::get for macOS 10.12 compatibility. - throw py::type_error("flags must be LoadFlags or int"); - } + LoadFlags flags = std::visit(overloaded { + [&](FT_Int32 value) { + auto api = py::module_::import("matplotlib._api"); + auto warn = api.attr("warn_deprecated"); + warn("since"_a="3.10", "name"_a="flags", "obj_type"_a="parameter as int", + "alternative"_a="LoadFlags enum values"); + return static_cast(value); + }, + [](LoadFlags value) { + return value; + } + }, flags_or_int); self->load_glyph(glyph_index, static_cast(flags)); diff --git a/src/mplutils.h b/src/mplutils.h index 95d9a2d9eb90..a5e03b6d955a 100644 --- a/src/mplutils.h +++ b/src/mplutils.h @@ -49,14 +49,18 @@ enum { }; #ifdef __cplusplus // not for macosx.m -// 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. #include #include namespace py = pybind11; using namespace pybind11::literals; +// Helper for std::visit. +template struct overloaded : Ts... { using Ts::operator()...; }; +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) { @@ -95,11 +99,10 @@ inline void check_trailing_shape(T array, char const* name, long d1, long d2) } } -/* In most cases, code should use safe_first_shape(obj) instead of obj.shape(0), since - safe_first_shape(obj) == 0 when any dimension is 0. */ +// In most cases, code should use safe_first_shape(obj) instead of +// obj.shape(0), since safe_first_shape(obj) == 0 when any dimension is 0. template -py::ssize_t -safe_first_shape(const py::detail::unchecked_reference &a) +py::ssize_t safe_first_shape(const py::detail::unchecked_reference &a) { bool empty = (ND == 0); for (py::ssize_t i = 0; i < ND; i++) {