-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Dedupe some C++ templates #17497
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dedupe some C++ templates #17497
Conversation
We can use C++11 now, I think; maybe the bug will also be fixed too? |
Should that be mentioned somewhere in the devdocs? |
Probably, I think we just discussed this in Gitter only. |
OTOH, it looks like MSVC still doesn't like my templates, so perhaps the entire thing is moot... |
Looks like I got this to work again with a healthy dose of std::conditional instead of using std::enable_if... |
@anntzer how convinced are we that all these are tested? |
|
||
using span_gen_t = typename type_mapping_t::template span_gen_nn_type<image_accessor_t, arbitrary_interpolator_t>; | ||
using span_conv_t = agg::span_converter<span_gen_t, span_conv_alpha_t>; | ||
using nn_renderer_t = agg::renderer_scanline_aa<renderer_t, span_alloc_t, span_conv_t>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are the three lines that are not tested (as in has line coverage). I guess it would be worthwhile to add a test for this case (nearest interpolation, not affine) outside of this PR?
I found an even shorter way to write this, which goes on top of #25152, so let's wait for that one first. |
OK, now rebased. Let's see what CI says. |
fwiw codecov (which does handle C++ coverage) seems happy now. |
FYI, here is the link to Scipy's docs on C++ version support. So it seems like they are willing to push the bounds even further and go with some C++17 constructs already. We might want to link to that somewhere in the docs if we want to say we support XX version. |
Good link! Especially the following:
So I guess the question is what the reasons to pin the C++ numbers that low is. Are there relevant platforms where it is hard (inconvenient) to get a reasonably new C++ compiler? |
(type == NPY_INT16) ? resample<agg::rgba16> : | ||
(type == NPY_FLOAT32) ? resample<agg::rgba32> : | ||
(type == NPY_FLOAT64) ? resample<agg::rgba64> : | ||
nullptr)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there an obvious benefit to write it is this way?
(In addition to "hide" that there are no tests for short? Which I realize is not the purpose, but a direct consequence. A single raise I can see, but other than that, the previous code was easier to read for me anyway, But then I know that you like these constructs in general (and I don't...), so I can ignore that if there is a benefit.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For me the point of writing it like this is to allow focusing on what matters (the NPY_FOO -> agg:type mapping, each pair of which now appears on a single line), and reduce line noise (input/output/params, which are the same every time, and the breaks; which add no info).
The fact that this "hides" that a branch is not taken is not a plus (if anything it's a minus), we should enable branch coverage in C++ too if possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rather like it in this form in this particular case. I think it is quite readable. Did the code here get updated in the meantime as I don't see anything getting hidden?
I would be happy to allow a more recent C++ standard but I don't feel particularly strongly about it; the point here was more to make sure we don't go beyond whatever we agree on. |
We do document that we currently require c++11 at https://matplotlib.org/stable/devel/dependencies.html#c-compiler
along with which versions of major compilers (gcc, clang, visual studio). |
It looks like this is ready to go, pending review by a C++ wizard, which rules me out. If I understand correctly, the current documentation that we require C++11 is adequate for this PR; I don't see anything in the comments indicating that more discussion is really needed. |
@ianthomas23 willing to look at this? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks OK to me apart from nitpicking typo.
src/_image_resample.h
Outdated
@@ -500,240 +500,58 @@ typedef enum { | |||
} interpolation_e; | |||
|
|||
|
|||
template <typename T> | |||
class type_mapping; | |||
// T is rgba iff it has an T::r field. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// T is rgba iff it has an T::r field. | |
// T is rgba if it has an T::r field. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this iff = if and only if?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Evidently that wasn't clear to me.
Maybe it should be written out in full? The templated code is essentially obfuscated, so maybe the English should try to go in the other direction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there more that can be done to clarify the code, via some combination of comments and changes in the code itself?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, not really. Clever templated C++ is usually difficult to understand unless you are seeing it regularly.
This is a significant reduction in the number of lines of code. Each line is more difficult to understand, but the reduction in the number of lines means it is probably easier to understand overall.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Evidently that wasn't clear to me.
Good point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, wrote out "if and only if".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ianthomas23 Do you think writing type_mapping as
template<typename color_type, typename Enable = void>
struct type_mapping {};
template<typename color_type>
struct type_mapping<color_type, typename std::enable_if<!is_grayscale<color_type>::value>::type>
{
using blender_type = typename std::conditional<
std::is_same<color_type, agg::rgba8>::value,
fixed_blender_rgba_plain<color_type, agg::order_rgba>,
agg::blender_rgba_plain<color_type, agg::order_rgba>
>::type;
using pixfmt_type =
agg::pixfmt_alpha_blend_rgba<blender_type, agg::rendering_buffer>;
using pixfmt_pre_type = typename
agg::pixfmt_alpha_blend_rgba<
typename std::conditional<
std::is_same<color_type, agg::rgba8>::value,
fixed_blender_rgba_pre<color_type, agg::order_rgba>,
agg::blender_rgba_pre<color_type, agg::order_rgba>
>::type,
agg::rendering_buffer>;
template<typename A> using span_gen_affine_type =
agg::span_image_resample_rgba_affine<A>;
template<typename A, typename B> using span_gen_filter_type =
agg::span_image_filter_rgba<A, B>;
template<typename A, typename B> using span_gen_nn_type =
agg::span_image_filter_rgba_nn<A, B>;
};
template<typename color_type>
struct type_mapping<color_type, typename std::enable_if<is_grayscale<color_type>::value>::type>
{
using blender_type = agg::blender_gray<color_type>;
using pixfmt_type = agg::pixfmt_alpha_blend_gray<blender_type, agg::rendering_buffer>;
using pixfmt_pre_type = pixfmt_type;
template<typename A> using span_gen_affine_type =
agg::span_image_resample_gray_affine<A>;
template<typename A, typename B> using span_gen_filter_type =
agg::span_image_filter_gray<A, B>;
template<typename A, typename B> using span_gen_nn_type =
agg::span_image_filter_gray_nn<A, B>;
};
(essentially getting rid of most std::conditional in favor of partial template specialization) reads better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very slightly. But as there is already use of std::conditional
maybe it is better to keep all of them for consistency. On balance, I'd be inclined to keep it as it is (commit 9644a9c
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Note that I could also split out the agg::rgba8 case as another template specialization (duplicating some of the rgba type_mapping) to fully get rid of std::conditional, if you prefer.)
3314f4b
to
9644a9c
Compare
Do we have another C++-guru to review this or should someone just merge it? |
I have a fair amount of C++ experience. I'll try to get to it today, but I
can't make any guarantees.
…On Wed, Jun 14, 2023 at 5:25 AM Antony Lee ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In src/_image_resample.h
<#17497 (comment)>
:
> @@ -500,240 +500,58 @@ typedef enum {
} interpolation_e;
-template <typename T>
-class type_mapping;
+// T is rgba iff it has an T::r field.
(Note that I could also split out the agg::rgba8 case as another template
specialization (duplicating some of the rgba type_mapping) to fully get rid
of std::conditional, if you prefer.)
—
Reply to this email directly, view it on GitHub
<#17497 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AACHF6ADVTIBJKL7EWRKDR3XLF7QHANCNFSM4NIL2E2Q>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I didn't have time to verify that the deduplication was done exactly correctly, I think this is quite readable. The original code probably should have had some narrative comment explaining the reason and/or purpose of the different template classes, and at least now I can better see what the differences are and get a sense of the reasons. It probably wouldn't hurt to add a short explanation of what is going on, though.
const T *input, int in_width, int in_height, | ||
T *output, int out_width, int out_height, | ||
const void *input, int in_width, int in_height, | ||
void *output, int out_width, int out_height, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why change to void
? Why not color_type
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is so that the big switch below ((type == NPY_UINT8) ? resample<agg::gray8> : ...
) works: as things are written currently, all template instantiations of resample have the same (function pointer) type (*void)(const void*, int, ...)
. If I explicitly put color_type instead, then they would have different types (the first argument would have different types) and the ternary would not work.
re: the ternary below: no, there was no other update I think.
(type == NPY_INT16) ? resample<agg::rgba16> : | ||
(type == NPY_FLOAT32) ? resample<agg::rgba32> : | ||
(type == NPY_FLOAT64) ? resample<agg::rgba64> : | ||
nullptr)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rather like it in this form in this particular case. I think it is quite readable. Did the code here get updated in the meantime as I don't see anything getting hidden?
@WeatherGod thanks! No hurry really. More that I didn't want to wait for someone that didn't exist... |
I think Agg, to a large extent, is architected around chaining templates like it is done in resample() (like it or not); all type_mapping is doing is providing the types to fill in these templates. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That explanation makes sense to me. I'm fine with this.
Failure seems unrelated?
|
PR Summary
Now that we're messing up with C++ image resampling... ;)
1st commit: Dedupe some C++ templates.
2nd commit: Make sure we don't use C++14 accidentally. (Was originally: don't use C++11 accidentally.)
Edit: apparently running into a MSVC bug (per https://devblogs.microsoft.com/cppblog/expression-sfinae-improvements-in-vs-2015-update-3/); will look into it...
PR Checklist