-
Notifications
You must be signed in to change notification settings - Fork 434
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
Make rclcpp::Duration support scale operation #437
Conversation
rclcpp/src/rclcpp/duration.cpp
Outdated
Duration | ||
Duration::operator*(double scale) const | ||
{ | ||
return Duration(rcl_duration_.nanoseconds * scale); |
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.
Please add underflow and overflow checking here. We are checking + and - operations we should check * too.
rclcpp/test/test_duration.cpp
Outdated
@@ -54,6 +54,10 @@ TEST(TestDuration, operators) { | |||
EXPECT_EQ(sub.nanoseconds(), (rcl_duration_value_t)(young.nanoseconds() - old.nanoseconds())); | |||
EXPECT_EQ(sub, young - old); | |||
|
|||
rclcpp::Duration scale = old * 3; | |||
EXPECT_EQ(scale.nanoseconds(), (rcl_duration_value_t)(old.nanoseconds() * 3)); | |||
EXPECT_EQ(scale, old * 3); |
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 line doesn't seem to test anything. scale
is defined just as old * 3
two lines above. So this will always be true
if the operator is deterministic.
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 know it, the reason I add this check is that the tests for operation +/- both include it, just keep consistent
rclcpp::Duration add = old + young;
EXPECT_EQ(add.nanoseconds(), (rcl_duration_value_t)(old.nanoseconds() + young.nanoseconds()));
EXPECT_EQ(add, old + young);
rclcpp::Duration sub = young - old;
EXPECT_EQ(sub.nanoseconds(), (rcl_duration_value_t)(young.nanoseconds() - old.nanoseconds()));
EXPECT_EQ(sub, young - old);
Thanks @jwang11 for the contribution. |
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 had a few comments inline on the range checking. And we should make sure to have unit tests for the range checking to validate that they're working.
rclcpp/src/rclcpp/duration.cpp
Outdated
bounds_check_duration_scale(int64_t dns, double scale, uint64_t max) | ||
{ | ||
auto abs_dns = (uint64_t)std::abs(dns); | ||
auto abs_scale = (uint64_t)std::abs(scale); |
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.
Casting this to be an unsigned integer I think is going to do the wrong thing here. It's used lower as if it's still a float.
rclcpp/src/rclcpp/duration.cpp
Outdated
auto abs_scale = (uint64_t)std::abs(scale); | ||
|
||
if ((abs_scale >= 1.0 && abs_dns > (uint64_t)(max / abs_scale)) || | ||
(abs_scale < 1.0 && (uint64_t)(abs_dns * abs_scale > max))) |
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.
If we're below an absolute scaling factor of 1 the value can only get smaller which will never overflow or underflow a signed integer. I think this whole 2nd case can be dropped.
rclcpp/src/rclcpp/duration.cpp
Outdated
(abs_scale < 1.0 && (uint64_t)(abs_dns * abs_scale > max))) | ||
{ | ||
if ((dns > 0 && scale > 0) || (dns < 0 && scale < 0)) { | ||
throw std::overflow_error("addition leads to int64_t overflow"); |
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 should be 'duration scaling' not 'addition'
rclcpp/src/rclcpp/duration.cpp
Outdated
if ((dns > 0 && scale > 0) || (dns < 0 && scale < 0)) { | ||
throw std::overflow_error("addition leads to int64_t overflow"); | ||
} else { | ||
throw std::underflow_error("addition leads to int64_t underflow"); |
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 should be 'duration scaling' not 'addition'
@@ -54,6 +54,9 @@ TEST(TestDuration, operators) { | |||
EXPECT_EQ(sub.nanoseconds(), (rcl_duration_value_t)(young.nanoseconds() - old.nanoseconds())); | |||
EXPECT_EQ(sub, young - old); | |||
|
|||
rclcpp::Duration scale = old * 3; | |||
EXPECT_EQ(scale.nanoseconds(), (rcl_duration_value_t)(old.nanoseconds() * 3)); |
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 should also have tests for the underflow and overflow. In particular I'd like to see at least all 4 cases of underflow and overflow with positive and negative scaling factors.
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.
Thanks for comments, they are all reasonable.
Please make sure to run the unit tests of the package locally. The linters will e.g. enforce to use C++ cast rather than C casts. |
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.
Windows also failed with a gateway issue so i'm going to retrigger it.
bounds_check_duration_scale(int64_t dns, double scale, uint64_t max) | ||
{ | ||
auto abs_dns = static_cast<uint64_t>(std::abs(dns)); | ||
auto abs_scale = std::abs(scale); |
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 apparently ambiguous on OSX: https://ci.ros2.org/job/ci_osx/3306/consoleFull
03:04:22 [ 38%] Building CXX object CMakeFiles/rclcpp.dir/src/rclcpp/contexts/default_context.cpp.o
03:04:23 [ 39%] Building CXX object CMakeFiles/rclcpp.dir/src/rclcpp/duration.cpp.o
03:04:23 /Users/osrf/jenkins/workspace/ci_osx/ws/src/ros2/rclcpp/rclcpp/src/rclcpp/duration.cpp:189:20: error: call to 'abs' is ambiguous
03:04:23 auto abs_scale = std::abs(scale);
03:04:23 ^~~~~~~~
03:04:23 /usr/include/stdlib.h:137:6: note: candidate function
03:04:23 int abs(int) __pure2;
03:04:23 ^
03:04:23 /Library/Developer/CommandLineTools/usr/include/c++/v1/stdlib.h:115:44: note: candidate function
03:04:23 inline _LIBCPP_INLINE_VISIBILITY long abs( long __x) _NOEXCEPT {return labs(__x);}
03:04:23 ^
03:04:23 /Library/Developer/CommandLineTools/usr/include/c++/v1/stdlib.h:117:44: note: candidate function
03:04:23 inline _LIBCPP_INLINE_VISIBILITY long long abs(long long __x) _NOEXCEPT {return llabs(__x);}
03:04:23 ^
03:04:23 /Users/osrf/jenkins/workspace/ci_osx/ws/src/ros2/rclcpp/rclcpp/src/rclcpp/duration.cpp:203:13: error: no member named 'isnormal' in namespace 'std'
03:04:23 if (!std::isnormal(scale)) {
03:04:23 ~~~~~^
03:04:23 2 errors generated.
03:04:23 make[2]: *** [CMakeFiles/rclcpp.dir/build.make:212: CMakeFiles/rclcpp.dir/src/rclcpp/duration.cpp.o] Error 1
03:04:23 make[1]: *** [CMakeFiles/Makefile2:855: CMakeFiles/rclcpp.dir/all] Error 2
03:04:23 make: *** [Makefile:141: all] Error 2
03:04:23
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.
You want std::fabs
: http://en.cppreference.com/w/cpp/numeric/math/fabs
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.
Though, it should probably work anyways...
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.
You might just need to #include <cmath>
. The standard seems to have been inconsistent on that in the past, which would allow for two different implementations to give you different results.
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.
Ok, I'll explicitly add #include , thank you
rclcpp/src/rclcpp/duration.cpp
Outdated
this->rcl_duration_.nanoseconds, | ||
scale, | ||
std::numeric_limits<rcl_duration_value_t>::max()); | ||
return Duration(rcl_duration_.nanoseconds * scale); |
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 giving a warning on windows: http://ci.ros2.org/job/ci_windows/4078/
'argument': conversion from 'double' to 'rcl_duration_value_t', possible loss of data
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 guess a explicit cast can avoid this warning, however locally I have no windows build environment, please help re-trigger CI to check.
CI with 3dacb27 |
Windows warning disappeared. From log, the failure on macOS and Windows are python test script to execute command, which should not relate to this PR. |
rclcpp/src/rclcpp/duration.cpp
Outdated
Duration | ||
Duration::operator*(double scale) const | ||
{ | ||
if (!std::isnormal(scale)) { |
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.
@tfoote is it appropriate to prevent users from having a scale of 0 here? Seems too strict
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.
Yeah, a zero scale value would be valid. I think isfinite
would be a better check.
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 agree, in theory zero is valid to scale although this operation is meaningless. Ok, I'll update code from isnormal to isfinite which allow both normal and zero.
Duration scale is a convinient operation which had supported in ROS. This commit make ROS2 support it. Signed-off-by: jwang <jing.j.wang@intel.com>
Signed-off-by: jwang <jing.j.wang@intel.com>
*) Keep abs_scale double, not cast to uint64_t *) drop unnecessary check under condition abs_scale < 1.0 *) Correct warning text *) Add overflow/underflow test when do scaling Signed-off-by: jwang <jing.j.wang@intel.com>
Signed-off-by: jwang <jing.j.wang@intel.com>
Signed-off-by: jwang <jing.j.wang@intel.com>
Signed-off-by: jwang <jing.j.wang@intel.com>
If this PR is still under review by someone? Actually I have a porting work from ROS1 to ROS2 which reply on this patch, is it possible expedite the process? thanks |
@jwang11 It's good to comment back when the existing reviewers comments are addressed so we know it's ready to look at again. I've restarted CI: |
Unfortunately the branch was now out of date. I rebased it to https://github.com/ros2/rclcpp/tree/duration_scale And started CI again. |
The only failures are composition 2 on osx and 4 on windows: ros2/build_farmer#86 |
Squashed rebased and merged in af6e86c |
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
Duration scale is a convinient operation which had supported in ROS.
This commit make ROS2 support it.
Signed-off-by: jwang jing.j.wang@intel.com