Skip to content

Statically allocate string concatenations using FMT formatting #2205

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

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

mini-1235
Copy link

closes #1906

Copy link
Member

@saikishor saikishor left a comment

Choose a reason for hiding this comment

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

@mini-1235 can you check other parts of code where these kinds of strings are built and can you replace them with fmt ?

Thank you

@mini-1235
Copy link
Author

mini-1235 commented Apr 26, 2025

@mini-1235 can you check other parts of code where these kinds of strings are built and can you replace them with fmt ?

Thank you

@saikishor Do I need to do this for the tests as well?

@saikishor
Copy link
Member

@saikishor Do I need to do this for the tests as well?

I don't think it's needed for the tests!

Copy link
Contributor

mergify bot commented Apr 26, 2025

This pull request is in conflict. Could you fix it @mini-1235?

@mini-1235 mini-1235 force-pushed the patch-static-compile branch from e051b0b to 8332c26 Compare April 26, 2025 19:15
@mini-1235 mini-1235 requested a review from saikishor April 26, 2025 19:19
@mini-1235
Copy link
Author

I can see that the CI is reporting fmt not linking properly which I could not reproduce on my local machine, I will try to fix according to the CI

@bmagyar
Copy link
Member

bmagyar commented Apr 26, 2025

For the CI related issues this package may be a good resource to look into: https://github.com/facontidavide/ros2_logging_fmt

Probably there are some hints to find here leading to a solution.

@saikishor
Copy link
Member

/usr/bin/ld: CMakeFiles/test_joint_handle.dir/test/test_handle.cpp.o: in function `std::make_unsigned<long>::type fmt::v9::detail::to_unsigned<long>(long)':
  test_handle.cpp:(.text._ZN3fmt2v96detail11to_unsignedIlEENSt13make_unsignedIT_E4typeES4_[_ZN3fmt2v96detail11to_unsignedIlEENSt13make_unsignedIT_E4typeES4_]+0x25): undefined reference to `fmt::v9::detail::assert_fail(char const*, int, char const*)'
  collect2: error: ld returned 1 exit status
  gmake[2]: *** [CMakeFiles/test_joint_handle.dir/build.make:271: test_joint_handle] Error 1
  gmake[1]: *** [CMakeFiles/Makefile2:303: CMakeFiles/test_joint_handle.dir/all] Error 2
  gmake[1]: *** Waiting for unfinished jobs....

Seems like there is a linking issue. It is missing as a dependency or in the CMakeLists.txt
Please take a look

Copy link
Contributor

@christophfroehlich christophfroehlich left a comment

Choose a reason for hiding this comment

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

Thanks for the great work, I have some comments about dependencies.

I never used fmt, so I might be wrong: Which strings should be allocated with fmt? Every string where any variable is included with format specifiers, or +operator of std::string? I made a quick search and still found some occurrences without fmt
controller_manger.cpp: L117, L130, L137, L591, L598, L607, L1357, L1378, L1380, L1424, L1430, L1520, L1574, L1664, L1677, L1690, etc
I suggest searching once again for %s, %d, %f, %lu, throw?


<build_depend>rcutils</build_depend>
<exec_depend>rcutils</exec_depend>

<test_depend>ament_cmake_gmock</test_depend>
<test_depend>ros2_control_test_assets</test_depend>
<test_depend>fmt</test_depend>
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
<test_depend>fmt</test_depend>

this is not necessary if there is already a depend for it

Copy link
Author

Choose a reason for hiding this comment

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

fixed


<test_depend>ament_cmake_gmock</test_depend>
<test_depend>fmt</test_depend>
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
<test_depend>fmt</test_depend>

this is not necessary if there is already a depend for it

Copy link
Author

Choose a reason for hiding this comment

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

fixed

@@ -15,10 +15,10 @@
#ifndef HARDWARE_INTERFACE__COMPONENT_PARSER_HPP_
#define HARDWARE_INTERFACE__COMPONENT_PARSER_HPP_

#include <fmt/compile.h>
#include <string>
#include <unordered_map>
#include <vector>
Copy link
Contributor

Choose a reason for hiding this comment

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

please re-add this new-line (google code style)

Copy link
Author

Choose a reason for hiding this comment

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

Do you mean two new lines after #define? Or a new line after `<fmt/compile.h>?

Copy link
Author

Choose a reason for hiding this comment

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

I have reformat the header files in the latest commit, can you please check if that is what you mean @christophfroehlich ?


<test_depend>ament_cmake_gmock</test_depend>
<test_depend>generate_parameter_library</test_depend>
<test_depend>launch_ros</test_depend>
<test_depend>launch_testing_ament_cmake</test_depend>
<test_depend>fmt</test_depend>
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
<test_depend>fmt</test_depend>

this is not necessary if there is already a depend for it

Copy link
Author

Choose a reason for hiding this comment

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

fixed


<test_depend>ament_cmake_gmock</test_depend>
<test_depend>ros2_control_test_assets</test_depend>
<test_depend>fmt</test_depend>
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
<test_depend>fmt</test_depend>

this is not necessary if there is already a depend for it

Copy link
Author

Choose a reason for hiding this comment

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

fixed

Comment on lines 672 to 673
fmt::format(
FMT_COMPILE("Failed to set the initial state of the component : %s to %s"),
Copy link
Contributor

@christophfroehlich christophfroehlich Apr 27, 2025

Choose a reason for hiding this comment

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

Is %s valid fmt syntax? Here they only use it with fmt::fprintf

Copy link
Author

Choose a reason for hiding this comment

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

fixed

Copy link
Member

@saikishor saikishor left a comment

Choose a reason for hiding this comment

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

In general, I don't think we need to add target_link_libraries of fmt::fmt every where, in most cases adding to THIS_PACKAGE_INCLUDE_DEPENDS alone should do, as it is being used in the ament_target_dependencies

I also observed the missing include of fmt in the controller_interface and controller_manager along with the dependency in package.xml and changes in CMakeLists.txt -> We need this as we want to be with "include-what-you-use" principle

@@ -40,6 +41,7 @@ target_include_directories(hardware_interface PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/hardware_interface>
)
target_link_libraries(hardware_interface PRIVATE fmt::fmt)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
target_link_libraries(hardware_interface PRIVATE fmt::fmt)

ament_target_dependencies should alone be enough

Copy link
Author

Choose a reason for hiding this comment

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

fixed

Copy link
Member

Choose a reason for hiding this comment

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

@mini-1235 next time please commit the changes directly from the suggestions above. you can do so by clicking commit button in the UI. If you go to files tab, you can groups changes together and commit it in a single commit

Copy link
Author

Choose a reason for hiding this comment

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

@mini-1235 next time please commit the changes directly from the suggestions above. you can do so by clicking commit button in the UI. If you go to files tab, you can groups changes together and commit it in a single commit

Ok!

Comment on lines 75 to 76
target_link_libraries(test_joint_handle hardware_interface fmt::fmt)
ament_target_dependencies(test_joint_handle rcpputils fmt)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
target_link_libraries(test_joint_handle hardware_interface fmt::fmt)
ament_target_dependencies(test_joint_handle rcpputils fmt)
target_link_libraries(test_joint_handle hardware_interface)
ament_target_dependencies(test_joint_handle rcpputils)

Copy link
Author

Choose a reason for hiding this comment

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

fixed

Comment on lines 79 to 80
target_link_libraries(test_component_interfaces hardware_interface fmt::fmt)
ament_target_dependencies(test_component_interfaces ros2_control_test_assets fmt)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
target_link_libraries(test_component_interfaces hardware_interface fmt::fmt)
ament_target_dependencies(test_component_interfaces ros2_control_test_assets fmt)
target_link_libraries(test_component_interfaces hardware_interface)
ament_target_dependencies(test_component_interfaces ros2_control_test_assets)

Copy link
Author

Choose a reason for hiding this comment

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

fixed

Comment on lines 83 to 84
target_link_libraries(test_component_interfaces_custom_export hardware_interface fmt::fmt)
ament_target_dependencies(test_component_interfaces_custom_export ros2_control_test_assets fmt)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
target_link_libraries(test_component_interfaces_custom_export hardware_interface fmt::fmt)
ament_target_dependencies(test_component_interfaces_custom_export ros2_control_test_assets fmt)
target_link_libraries(test_component_interfaces_custom_export hardware_interface)
ament_target_dependencies(test_component_interfaces_custom_export ros2_control_test_assets)

I don't think it is needed in these files

Copy link
Author

Choose a reason for hiding this comment

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

fixed

Comment on lines 28 to 29
ament_target_dependencies(test_components hardware_interface pluginlib ros2_control_test_assets)
target_link_libraries(test_components fmt::fmt)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
ament_target_dependencies(test_components hardware_interface pluginlib ros2_control_test_assets)
target_link_libraries(test_components fmt::fmt)
ament_target_dependencies(test_components hardware_interface pluginlib ros2_control_test_assets fmt)

Copy link
Author

Choose a reason for hiding this comment

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

fixed

Comment on lines 41 to 42
target_link_libraries(test_resource_manager fmt::fmt)
ament_target_dependencies(test_resource_manager hardware_interface ros2_control_test_assets fmt)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
target_link_libraries(test_resource_manager fmt::fmt)
ament_target_dependencies(test_resource_manager hardware_interface ros2_control_test_assets fmt)
ament_target_dependencies(test_resource_manager hardware_interface ros2_control_test_assets fmt)

Copy link
Author

Choose a reason for hiding this comment

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

fixed

@@ -73,13 +74,14 @@ if(BUILD_TESTING)
find_package(rclcpp REQUIRED)

ament_add_gmock(joint_limits_rosparam_test test/joint_limits_rosparam_test.cpp)
target_link_libraries(joint_limits_rosparam_test joint_limits)
target_link_libraries(joint_limits_rosparam_test joint_limits fmt::fmt)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
target_link_libraries(joint_limits_rosparam_test joint_limits fmt::fmt)
target_link_libraries(joint_limits_rosparam_test joint_limits)

Copy link
Author

Choose a reason for hiding this comment

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

fixed, but looks like it needs a link in joint_limits, so I have addedtarget_link_libraries(joint_limits INTERFACE fmt::fmt), otherwise it will report

--- stderr: joint_limits                                
/usr/bin/ld: CMakeFiles/joint_limits_rosparam_test.dir/test/joint_limits_rosparam_test.cpp.o: undefined reference to symbol '_ZN3fmt2v96detail11assert_failEPKciS3_'
/usr/bin/ld: /lib/x86_64-linux-gnu/libfmt.so.9: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
gmake[2]: *** [CMakeFiles/joint_limits_rosparam_test.dir/build.make:222: joint_limits_rosparam_test] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:270: CMakeFiles/joint_limits_rosparam_test.dir/all] Error 2
gmake: *** [Makefile:146: all] Error 2


ament_add_gmock(joint_limits_urdf_test test/joint_limits_urdf_test.cpp)
target_link_libraries(joint_limits_urdf_test joint_limits)
target_link_libraries(joint_limits_urdf_test joint_limits fmt::fmt)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
target_link_libraries(joint_limits_urdf_test joint_limits fmt::fmt)
target_link_libraries(joint_limits_urdf_test joint_limits)

Copy link
Author

Choose a reason for hiding this comment

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

fixed

Comment on lines 80 to 81
PRIVATE PARAMETERS_FILE_PATH="${CMAKE_CURRENT_LIST_DIR}/test/"
)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
PRIVATE PARAMETERS_FILE_PATH="${CMAKE_CURRENT_LIST_DIR}/test/"
)
PRIVATE PARAMETERS_FILE_PATH="${CMAKE_CURRENT_LIST_DIR}/test/")

Any particular reason for this change?

Copy link
Author

Choose a reason for hiding this comment

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

Nope, I have reverted it

@@ -27,6 +28,7 @@ target_include_directories(transmission_interface PUBLIC
$<INSTALL_INTERFACE:include/transmission_interface>
)
ament_target_dependencies(transmission_interface PUBLIC ${THIS_PACKAGE_INCLUDE_DEPENDS})
target_link_libraries(transmission_interface PUBLIC fmt::fmt)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
target_link_libraries(transmission_interface PUBLIC fmt::fmt)

As you have added it to the THIS_PACKAGE_INCLUDE_DEPENDS, it should be automatic

Copy link
Author

Choose a reason for hiding this comment

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

fixed

@mini-1235
Copy link
Author

I never used fmt, so I might be wrong: Which strings should be allocated with fmt? Every string where any variable is included with format specifiers, or +operator of std::string?

I think allocating strings are only required when concatenating? Those only with format specifiers like

 RCLCPP_DEBUG(
      rclcpp::get_logger("ControllerManager::utils"),
      "Required interface '%s' with prefix '%s' is not a chain interface.", interface_name.c_str(),
      interface_prefix.c_str());

are not required, right? @saikishor

@saikishor
Copy link
Member

I think allocating strings are only required when concatenating? Those only with format specifiers like

 RCLCPP_DEBUG(
      rclcpp::get_logger("ControllerManager::utils"),
      "Required interface '%s' with prefix '%s' is not a chain interface.", interface_name.c_str(),
      interface_prefix.c_str());

are not required, right? @saikishor

Yes, it is not required for such cases

Copy link
Contributor

mergify bot commented May 2, 2025

This pull request is in conflict. Could you fix it @mini-1235?

@mini-1235
Copy link
Author

Sorry for the delayed reply here, I was building a new image on my local machine so that I can reproduce the errors reported by the CI :)

I suggest searching once again for %s, %d, %f, %lu, throw?

My searching pattern was + " / " +, I think that is enough to find all concatenation cases(?)

I also observed the missing include of fmt in the controller_interface and controller_manager along with the dependency in package.xml and changes in CMakeLists.txt -> We need this as we want to be with "include-what-you-use" principle

Added.

@saikishor
Copy link
Member

@mini-1235 can you rebase your changes on top of current master?

@mini-1235 mini-1235 force-pushed the patch-static-compile branch from b131097 to 2f7cb00 Compare May 2, 2025 17:39
Copy link
Member

@saikishor saikishor left a comment

Choose a reason for hiding this comment

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

@mini-1235 most of the files that have the fmt changes are missing the include of the header. Remember the "include_what_you_use" principle.

Also please fix the pre-commit

@@ -62,12 +62,12 @@ ChainableControllerInterface::export_state_interfaces()
{
if (interface.get_prefix_name().find(get_node()->get_name()) != 0)
Copy link
Member

Choose a reason for hiding this comment

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

@mini-1235 the include of the fmt is missing in this file

@@ -27,6 +28,7 @@ target_include_directories(joint_limits INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/joint_limits>
)
target_link_libraries(joint_limits INTERFACE fmt::fmt)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
target_link_libraries(joint_limits INTERFACE fmt::fmt)

Isn't the ament_target_dependencies doing the same thing?

Copy link
Author

Choose a reason for hiding this comment

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

If I commented out this line, I can see that the compiler reports a linking error:

--- stderr: joint_limits                                
/usr/bin/ld: CMakeFiles/joint_limits_rosparam_test.dir/test/joint_limits_rosparam_test.cpp.o: undefined reference to symbol '_ZN3fmt2v96detail11assert_failEPKciS3_'
/usr/bin/ld: /lib/x86_64-linux-gnu/libfmt.so.9: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
gmake[2]: *** [CMakeFiles/joint_limits_rosparam_test.dir/build.make:222: joint_limits_rosparam_test] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:270: CMakeFiles/joint_limits_rosparam_test.dir/all] Error 2
gmake: *** [Makefile:146: all] Error 2

I will discover why

Copy link

codecov bot commented May 2, 2025

Codecov Report

Attention: Patch coverage is 71.50000% with 57 lines in your changes missing coverage. Please review.

Project coverage is 88.94%. Comparing base (b2d9bf7) to head (2f7cb00).
Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
controller_manager/src/controller_manager.cpp 64.55% 28 Missing ⚠️
...r_interface/src/chainable_controller_interface.cpp 28.57% 10 Missing ⚠️
hardware_interface/src/component_parser.cpp 88.57% 4 Missing ⚠️
hardware_interface/src/resource_manager.cpp 60.00% 4 Missing ⚠️
.../include/hardware_interface/actuator_interface.hpp 81.81% 2 Missing ⚠️
...ce/include/hardware_interface/system_interface.hpp 81.81% 2 Missing ⚠️
joint_limits/src/joint_limits_helpers.cpp 0.00% 2 Missing ⚠️
...re_interface/include/hardware_interface/handle.hpp 85.71% 1 Missing ⚠️
...ce/include/hardware_interface/sensor_interface.hpp 80.00% 1 Missing ⚠️
...its/include/joint_limits/joint_limits_rosparam.hpp 75.00% 1 Missing ⚠️
... and 2 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2205      +/-   ##
==========================================
- Coverage   89.14%   88.94%   -0.20%     
==========================================
  Files         141      142       +1     
  Lines       16168    16483     +315     
  Branches     1386     1427      +41     
==========================================
+ Hits        14413    14661     +248     
- Misses       1227     1269      +42     
- Partials      528      553      +25     
Flag Coverage Δ
unittests 88.94% <71.50%> (-0.20%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
.../include/controller_manager/controller_manager.hpp 96.87% <ø> (ø)
...nt_limits/include/joint_limits/data_structures.hpp 75.86% <100.00%> (ø)
...re_interface/include/hardware_interface/handle.hpp 88.69% <85.71%> (-0.60%) ⬇️
...ce/include/hardware_interface/sensor_interface.hpp 85.00% <80.00%> (-4.59%) ⬇️
...its/include/joint_limits/joint_limits_rosparam.hpp 59.42% <75.00%> (ø)
...ansmission_interface/differential_transmission.hpp 80.73% <90.00%> (+0.92%) ⬆️
...ission_interface/four_bar_linkage_transmission.hpp 79.00% <90.00%> (+1.10%) ⬆️
.../include/hardware_interface/actuator_interface.hpp 90.96% <81.81%> (-3.16%) ⬇️
...ce/include/hardware_interface/system_interface.hpp 84.26% <81.81%> (-2.58%) ⬇️
joint_limits/src/joint_limits_helpers.cpp 88.23% <0.00%> (+2.02%) ⬆️
... and 4 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

mini-1235 and others added 2 commits May 3, 2025 22:05
Co-authored-by: Sai Kishor Kothakota <[email protected]>
Signed-off-by: mini-1235 <[email protected]>
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.

Statically allocate string concatinations using FMT
4 participants