Skip to content
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

Fix unary operators #237

Merged
merged 6 commits into from
Oct 12, 2022
Merged

Fix unary operators #237

merged 6 commits into from
Oct 12, 2022

Conversation

zwimer
Copy link
Contributor

@zwimer zwimer commented Oct 8, 2022

This fixes: #225

It also addresses #226 by renaming plus_plus to pre_increment or post_increment depending on which is called. It unfortunately does not fix the issue that the post increment operator requires an integer argument. I'm not sure how to fix that. If this is not ok, I can undo this fix and only let this PR address #225

@zwimer
Copy link
Contributor Author

zwimer commented Oct 8, 2022

Likewise, the above applies for minus_minus

@zwimer
Copy link
Contributor Author

zwimer commented Oct 8, 2022

@lyskov If you have a suggestion as to how to make the post_increment and post_decrement not take in a dummy-integer argument, please let me know and I'll do it!

To be clear, I change binder so that:

  1. ++x: operator++() no longer maps to plus_plus but instead maps to pre_increment
  2. x++: operator++(int) no longer maps to plus_plus but instead maps to post_increment

The issue is that dummy int C++ post-increment operator requires is taken literally by binder so post_increment needs an argument, even though it's just a dummy that is ignored.

@lyskov
Copy link
Member

lyskov commented Oct 10, 2022

@zwimer thank you for reporting this!

Re proposed solution: i thought about this and have rather mixed feeling about it: technically it possible to provide value for this dummy parameter (and for operator to use it) (see https://en.cppreference.com/w/cpp/language/operator_incdec). So why it might be more "pythonic" to avoid user to specify it, it in the same time disallow us to fully use the C++ code. Of course we could probably all agree that using this dummy int as parameter in C++ code is probably a bad idea in the first place... but as usual in such cases when we disallow such usage it will break someone workflow. (btw this point is nicely illustrate in https://xkcd.com/1172/)

@zwimer
Copy link
Contributor Author

zwimer commented Oct 11, 2022

@lyskov I think I was unclear. Right now, if you bind both ++a and a++, binder will generate something akin to:

cl.def("plus_plus", (struct T & (T::*)()) &T::operator++, "C++: T::operator++() --> struct T &", pybind11::return_value_policy::automatic);
cl.def("plus_plus", (struct T & (T::*)(int)) &T::operator++, "C++: T::operator++(int) --> struct T &", pybind11::return_value_policy::automatic, pybind11::arg(""));

This PR changes that to

cl.def("pre_incerement", (struct T & (T::*)()) &T::operator++, "C++: T::operator++() --> struct T &", pybind11::return_value_policy::automatic);
cl.def("post_increment", (struct T & (T::*)(int)) &T::operator++, "C++: T::operator++(int) --> struct T &", pybind11::return_value_policy::automatic, pybind11::arg(""));

I think it would be beneficial to somehow instead do:

cl.def("pre_incerement", (struct T & (T::*)()) &T::operator++, "C++: T::operator++() --> struct T &", pybind11::return_value_policy::automatic);
cl.def("post_increment", (struct T & (T::*)()) []()->T(*)(int) { return &T::operator++; }(), "C++: T::operator++(int) --> struct T &", pybind11::return_value_policy::automatic);

That is to say. Currently in python we have: a.plus_plus(); a.plus_plus(0) for pre/post increment. This PR would give a.pre_increment(); a.post_increment(0). I think it would be better if we could edit this PR so that python would only need a.pre_increment(); a.post_increment().

My problem is that I do not know how to go from what this PR does to the better solution of not requiring a dummy argument in the first place. While I do think the current PR is better than what is on master, if it is an easy fix to make this other improvement, I think that is best. Thoughts?

@zwimer
Copy link
Contributor Author

zwimer commented Oct 11, 2022

Oh, no, I think I see your point. Hmm, is it possible / a good idea to let that be a CLI flag? Or bind both?

@lyskov
Copy link
Member

lyskov commented Oct 11, 2022

Oh, no, I think I see your point. Hmm, is it possible / a good idea to let that be a CLI flag? Or bind both?

-- let me think about this. Right now my take is that both naming (plus_plus vs pre/post_increment) is rather arbitrary choice so it is hard to argue while one is better than another. And there is also issue of backward compatibility with such renaming so with all things being about equal using old names is preferable.

@zwimer
Copy link
Contributor Author

zwimer commented Oct 12, 2022

@lyskov For now I'll split this into 2 PRs. One that just fixes the bugs, and one that does this renaming.

@zwimer zwimer changed the title Fix unary operators + rename post/pre inc/decrement operators Fix unary operators Oct 12, 2022
@zwimer
Copy link
Contributor Author

zwimer commented Oct 12, 2022

@lyskov The ++ and -- stuff has been moved to #238 It is was removed from this PR. This PR is not exclusively the bug fixes of the unary ops.


{"operator->", "arrow"}, //
};
// Return the python operator that maps to the C++ operator; returns "" if no mapping exists
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please update comments, particularly elaborate on how selection of correct name from the vector will be correlated to number of arguments C++ operator have? Thanks,

Comment on lines 82 to 86
if (vec.size() == 1) { return vec[0]; }
const auto n = F.getNumParams();
if (vec.size() > n) {
return vec[n];
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered approach:

const auto n = F.getNumParams();
return n < vec.size() ? vec[n] : vec.back();
  • that way operators with non-standard type signature (more then two arguments, operator with default arguments) will also be bound and could be used in Python by calling corresponding magic-method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That will work as well, before if you wanted a catch all you would just put it as the sole argument but I can update it to this. I don't think it would make a difference since I don't recall C++ allowing additional operator parameters besides the ones I coded, but I suppose this is safer in case I missed anything.

Copy link
Member

@lyskov lyskov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! - Thank you for putting this together @zwimer !

@lyskov lyskov merged commit 956142d into RosettaCommons:master Oct 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unary +, -, * invalid conversion
2 participants