-
Notifications
You must be signed in to change notification settings - Fork 308
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
Change NdProducer::Dim
of axis_windows()
to Ix1
#1305
Conversation
src/iterators/windows.rs
Outdated
} | ||
|
||
|
||
impl<'a, A, D: Dimension> NdProducer for AxisWindows<'a, A, D> { |
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 the impl_ndproducer!
macro unable to handle this impl? (It seems to be used for Windows
.)
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.
almost, the methods raw_dim
, uget_ptr
, stride_of
and split_at
need a different implementation.
src/iterators/windows.rs
Outdated
let mut window = base.raw_dim(); | ||
window[axis_idx] = window_size; | ||
|
||
base.slice_each_axis_inplace(|ax_desc| { |
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 am not really happy about the code duplication between this method and Windows::new_with_stride
. Is it reasonably possible to have re-use without making this too hard to follow? (Windows::new_with_stride
also appears to have additional assertions/consistency checks?)
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.
There is a way to do this with reusing the Windows
struct. I don't know if it is more or less clean. It would look like this:
pub struct Windows<'a, A, D, V>{
base: ArrayView<'a, A, D>,
window: D,
strides: D,
version: V,
}
pub struct GeneralWindow;
pub struct AxisWindow{index: usize}
impl<'a, A, D: Dimension> NdProducer for Windows<'a, A, D, GeneralWindow> {
// the default impl
}
impl<'a, A, D: Dimension> NdProducer for Windows<'a, A, D, AxisWindow> {
// the impl for the Axis version
}
Now the methods axis_windows
and windows
would both return Windows
but with a different generic V
.
The struct GeneralWindow
and AxisWindow
need to be public, but they can be inaccessible from outside of the crate.
The assertions are not needed for AxisWindows::new
because we construct the variables strides
and window
locally. axsis_windows()
asserts that the axis is in range.
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.
The struct GeneralWindow and AxisWindow need to be public, but they can be inaccessible from outside of the crate.
This does sound reasonable to me
I would suggest making Windows
and AxisWindows
public and have both wrap an WindowsImpl
or something like that. (Meaning AxisWindows
contains two fields, index: usize
and impl: WindowsImpl
.)
I would also suggest replacing the term "version" by "variant" to avoid any confusion as "version" often has a temporal connotation.
I don't know if it is more or less clean.
Of course, if this just increases complexity for very little gain in the end, it might not be worth it, you just have to give it a try.
As an alternative, a simple helper function encapsulating the slice-in-place approach using references to the constituent fields of Windows
and AxisWindows
might work as well.
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 just updated to my suggested structure, It actually does reduce code duplication a lot.
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 am not a fan of exposing these implementation detail generics in the API, hence the suggestion to expose Windows
and AxisWindows
with the same generic parameters as before which contain a WindowsImpl/Inner/Whatever
as a private field. We should not leak this sharing of implementation in the public API.
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 thought that This does sound reasonable to [you] 🙁
We need to expose that there is a difference between the return type of windows()
and axis_windows()
there is no way around that.
This generic is not an implementation detail. It makes a real difference to the public API. Specifically it changes the behavior of <Windows as NdProducer>
and the Type of <Windows as NdProducer>::Dim
. Otherwise this PR would be pointless.
Now we have this solution which is as minimal as it gets.
Or the solution I had before. Sure I could try and faff around with some helper methods and structs to reduce code duplication for the cost of clarity and more boilerplate.
Having a WindowsInner/Impl/...
will break the impl_ndproducer!
macro, because base
needs to be a field of Windows
not of some wrapped 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.
We need to expose that there is a difference between the return type of windows() and axis_windows() there is no way around that.
Distinct return types is not the issue. It is return types which only differ in a type parameter which I have a problem with. I want the relation between these two return types to be fully opaque to the caller, so that e.g. we could also not have code reuse in their implementation without that affecting our API.
This generic is not an implementation detail. It makes a real difference to the public API. Specifically it changes the behavior of and the Type of ::Dim. Otherwise this PR would be pointless.
<Windows as NdProducer>::Dim
and <AxisWindows as NdProducer>::Dim
can be different without the outside world seeing any relation between those two types.
Having a WindowsInner/Impl/... will break the impl_ndproducer! macro, because base needs to be a field of Windows not of some wrapped type.
This is a real issue and maybe manually implementing NdProducer
as you did for AxisWindows
for both cases is simple enough, but maybe this actually reduces reuse. This is why I also suggested just using an external helper method with completely disjoint struct types.
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.
Thank you for clearing that up. This makes much more sense.
I just extracted the main logic of creating the base into a separate function, and call that from both constructors Windwos::new_with_stride()
and AxisWindows::new()
src/iterators/windows.rs
Outdated
fn stride_of(&self, axis: Axis) -> isize { | ||
assert_eq!(axis, Axis(0)); | ||
self.base.stride_of(Axis(self.axis_idx)) | ||
} | ||
|
||
fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { | ||
assert_eq!(axis, Axis(0)); | ||
let (a, b) = self.base.split_at(Axis(self.axis_idx), index); |
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.
Here I added assertions to check that this is only called with Axis(0)
. Do we need this? I could just ignore the axis argument, but that would be confusing when a user tries to call this with a different axis.
In general I am not super confident that this trait is correctly implemented, as there is a severe lack of documentation for a lot of the methods.
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 totally see what you mean. Assertions are a good idea.
Is there anything holding this back that I can do do address? The CI errors don't seem to be related to this PR. |
No. Sorry this is waiting on me finding time to look into it again. |
859cc2b
to
cf95ef9
Compare
Rebased and updated with rustfmt. Maintainer pushed to do that. Oh, and I squashed everything together. I had to do that, conflict resolution (with rebase) is a bit tedious otherwise. |
Move core logic of creating the Windows logic into seperate fn. fix some bugs, make the tests pass Reduce code duplication Specialize the `Windows` struct with a `variant` field to replace the `AxisWindows` struct
cf95ef9
to
21fb817
Compare
Thank you @jonasBoss and @adamreichold for the work on this one. It looks good to me, let's delay no further 🙂, I'll be ready to push the merge button soon. |
See #1304
This breaks the public API because the method
axis_windows()
now returns a newAxisWindows
struct. And the dimension of theNdProducer::Dim
will always beIx1
.If the old behavior is needed the method
windows()
can be used.I was thinking about adding a method
AxisWindows::into_windows(self) -> Windows
but I don't think that this will see much use. Let me know if i should implement it.