Skip to content

Commit

Permalink
Add support for NaN constants and defaults in ROS IDL
Browse files Browse the repository at this point in the history
* Add nan default example and tests
* Add NaN constant example and tests
* Test NaNs on float64 and float32 in C++

Signed-off-by: Ryan Friedman <[email protected]>

Signed-off-by: Ryan Friedman <[email protected]>

Signed-off-by: Ryan Friedman <[email protected]>
  • Loading branch information
Ryanf55 committed Mar 6, 2024
1 parent 0775e10 commit e7223a0
Show file tree
Hide file tree
Showing 10 changed files with 52 additions and 5 deletions.
10 changes: 10 additions & 0 deletions rosidl_adapter/test/test_constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ def test_constant_constructor():
with pytest.raises(ValueError):
Constant('bool', 'FOO', None)

# NaN is case insensitive in python, so test a few variations.
value = Constant('float32', 'FOO', 'nan')
value = Constant('float32', 'FOO', 'Nan')
value = Constant('float32', 'FOO', 'NaN')
value = Constant('float32', 'FOO', 'NAN')
value = Constant('float64', 'FOO', 'nan')
value = Constant('float64', 'FOO', 'Nan')
value = Constant('float64', 'FOO', 'NaN')
value = Constant('float64', 'FOO', 'NAN')


def test_constant_methods():
assert Constant('bool', 'FOO', '1') != 23
Expand Down
2 changes: 1 addition & 1 deletion rosidl_generator_c/resource/idl__struct.h.em
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ extern "C"
{
#endif
#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@#######################################################################
@# Handle message
@#######################################################################
Expand Down
11 changes: 9 additions & 2 deletions rosidl_generator_c/rosidl_generator_c/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from math import isnan

from rosidl_generator_type_description import parse_rihs_string
from rosidl_generator_type_description import RIHS01_HASH_VALUE_SIZE
Expand Down Expand Up @@ -209,10 +210,16 @@ def basic_value_to_c(type_, value):
return f'{value}ull'

if 'float' == type_.typename:
return f'{value}f'
if isnan(float(value)):
return 'nanf(\"\")'
else:
return f'{value}f'

if 'double' == type_.typename:
return f'{value}l'
if isnan(float(value)):
return 'nan(\"\")'
else:
return f'{value}l'

assert False, "unknown basic type '%s'" % type_

Expand Down
1 change: 1 addition & 0 deletions rosidl_generator_cpp/resource/idl__struct.hpp.em
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ include_directives = set()
#include <algorithm>
#include <array>
#include <limits>
#include <memory>
#include <string>
#include <vector>
Expand Down
8 changes: 6 additions & 2 deletions rosidl_generator_cpp/resource/msg__struct.hpp.em
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,12 @@ non_defaulted_zero_initialized_members = [
@[ if constant.type.typename in UNSIGNED_INTEGER_TYPES]@
u@
@[ end if]@
@[ elif constant.type.typename == 'float']@
@(constant.value)f@
@[ elif constant.type.typename == 'float' or constant.type.typename == 'double']@
@{
from rosidl_generator_cpp import primitive_value_to_cpp
val = primitive_value_to_cpp(constant.type, constant.value)
}@
@(val)@
@[ else]@
@(constant.value)@
@[ end if];
Expand Down
6 changes: 6 additions & 0 deletions rosidl_generator_cpp/rosidl_generator_cpp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# limitations under the License.

from ast import literal_eval
from math import isnan

from rosidl_parser.definition import AbstractGenericString
from rosidl_parser.definition import AbstractNestedType
Expand Down Expand Up @@ -195,6 +196,11 @@ def primitive_value_to_cpp(type_, value):
if type_.typename == 'boolean':
return 'true' if value else 'false'

if type_.typename in ['double', 'long double', 'float']:
# Handle special floating types here.
if isnan(float(value)):
return f'std::numeric_limits<{type_.typename}>::quiet_NaN()'

if type_.typename in [
'short', 'unsigned short',
'char', 'wchar',
Expand Down
2 changes: 2 additions & 0 deletions rosidl_generator_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ if(BUILD_TESTING)
${test_interface_files_MSG_FILES}
${test_interface_files_SRV_FILES}
msg/BasicIdl.idl
msg/NanValueConstant.msg
msg/NanValueDefault.msg
msg/SmallConstant.msg
ADD_LINTER_TESTS
SKIP_INSTALL
Expand Down
2 changes: 2 additions & 0 deletions rosidl_generator_tests/msg/NanValueConstant.msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
float32 FLOAT32_NAN=NaN
float64 FLOAT64_NAN=nan
2 changes: 2 additions & 0 deletions rosidl_generator_tests/msg/NanValueDefault.msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
float32 float32_nan NaN
float64 float64_nan nan
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
#include "rosidl_generator_tests/msg/multi_nested.hpp"
#include "rosidl_generator_tests/msg/nested.hpp"
#include "rosidl_generator_tests/msg/small_constant.hpp"
#include "rosidl_generator_tests/msg/nan_value_constant.hpp"
#include "rosidl_generator_tests/msg/nan_value_default.hpp"
#include "rosidl_generator_tests/msg/strings.hpp"
#include "rosidl_generator_tests/msg/unbounded_sequences.hpp"
#include "rosidl_generator_tests/msg/w_strings.hpp"
Expand Down Expand Up @@ -475,6 +477,11 @@ TEST(Test_messages, constants_assign) {
ASSERT_EQ(x, rosidl_generator_tests::msg::SmallConstant::FLOAT32_CONST);
}

TEST(Test_messages, nan_constants) {
ASSERT_TRUE(std::isnan(rosidl_generator_tests::msg::NanValueConstant::FLOAT32_NAN));
ASSERT_TRUE(std::isnan(rosidl_generator_tests::msg::NanValueConstant::FLOAT64_NAN));
}

// Defaults
TEST(Test_messages, defaults) {
rosidl_generator_tests::msg::Defaults message;
Expand All @@ -501,6 +508,12 @@ TEST(Test_messages, defaults) {
TEST_BASIC_TYPE_FIELD_ASSIGNMENT(message, uint64_value, 50000000ull, UINT64_MAX);
}

TEST(Test_messages, nan_defaults) {
rosidl_generator_tests::msg::NanValueDefault nan_val_default;
ASSERT_TRUE(std::isnan(nan_val_default.float32_nan));
ASSERT_TRUE(std::isnan(nan_val_default.float64_nan));
}

// String array with default values
TEST(Test_messages, string_arrays_default) {
rosidl_generator_tests::msg::Arrays message;
Expand Down

0 comments on commit e7223a0

Please sign in to comment.