LibraryLoadException when instantiating nested ClassLoader within custom SystemInterface implementation

asked 2022-08-13 12:55:17 -0600

updated 2022-08-16 10:01:42 -0600

I have a hardware plugin (let's call it MyHardware) that implements hardware_interface::SystemInterface and is automatically loaded via the pluginlib framework at runtime as per the ros2_control tag below:

 <ros2_control name="my_robot" type="system">
    <hardware>
      <plugin>my_hardware_package::MyHardware</plugin>
    </hardware>
   <joint ... />
   <joint ... />
</ros2_control>

I get the following error:

[somenode-3] terminate called after throwing an instance of 'pluginlib::LibraryLoadException'
[somenode-3]   what():  Failed to load library /home/user/code/colcon_ws/install/my_hardware_package/lib/libmy_hardware_package.so. Make sure that you are calling the PLUGINLIB_EXPORT_CLASS macro in the library code, and that names are consistent between this macro and your XML. Error string: Could not load library dlopen error: /home/user/code/colcon_ws/install/my_hardware_package/lib/libmy_hardware_package.so: undefined symbol: _ZTIN25my_nested_interface_package14MyNestedInterfaceE, at /tmp/binarydeb/ros-galactic-rcutils-4.0.3/src/shared_library.c:99

My setup is peculiar because I am instantiating another ClassLoader<my_nested_interface_package::MyNestedInterface> instance within my MyHardware class, to invoke additional dynamic code. I have narrowed down the cause of the exception to the following initialization statement within my MyHardware class:

using namespace pluginlib;
using namespace NestedInterface = my_nested_interface_package::MyNestedInterface;

namespace my_hardware_package {
class MyHardware : public hardware_interface::SystemInterface {
    ...

    CallbackReturn RealworldSystem::on_init(const HardwareInfo& info) {

       ...<log-statements-here>...

       ...<PROBLEMATIC-CODE-HERE>...
       this->nested_plugin_loader = std::shared_ptr<ClassLoader<NestedInterface>> (
               new ClassLoader<NestedInterface> (
                   "my_nested_interface_package",
                   "my_nested_interface_package::MyNestedInterface"
               )
       );
       ...</PROBLEMATIC-CODE-HERE>...
    }

    ...
};
} // namespace

#include "pluginlib/class_list_macros.hpp"  // NOLINT
PLUGINLIB_EXPORT_CLASS(
        my_hardware_package::MyHardware,
        hardware_interface::SystemInterface
)

Observations:

  1. The error occurs even without any call to ClassLoader::createInstance() within the code, so neither the instantiation nor the use of the actual nested plugin figures into the error.
  2. If I include the PROBLEMATIC-CODE-HERE section (and recompile), I get the LibraryLoadException for the entire my_hardware_package (so, even the log-statements-here section doesn't get printed). Merely adding that statement makes the my_hardware_package unloadable, with the LibraryLoadException.
  3. However, if I comment out the PROBLEMATIC-CODE-HERE section (and recompile), my_hardware_package::MyHardware gets loaded successfully and the log-statements-here section runs normally, with no LibraryLoadException.

The CMakeLists.txt for my_hardware_package is as follows:

cmake_minimum_required(VERSION 3.8)
project(my_hardware_package)

###################################################
#           Compilation Flags/Options
###################################################
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

###################################################
#           Package dependencies
###################################################
find_package(ament_cmake REQUIRED)
find_package(ament_cmake_core REQUIRED)
find_package(ament_index_cpp REQUIRED)
find_package(hardware_interface REQUIRED)
find_package(pluginlib REQUIRED)
find_package(rclcpp REQUIRED)

find_package(my_nested_interface_package REQUIRED)

###################################################
#           Libraries
###################################################
add_library(${PROJECT_NAME} SHARED
  src/my_hardware.cpp
)
target_include_directories(${PROJECT_NAME} PRIVATE include)
ament_target_dependencies(${PROJECT_NAME}
  ament_index_cpp
  hardware_interface
  pluginlib
  rclcpp

  my_nested_interface_package
)
target_compile_definitions(${PROJECT_NAME} PRIVATE "MY_HARDWARE_PLUGIN_PACKAGE_BUILDING_LIBRARY")

###################################################
#           Installation Targets
###################################################
install(TARGETS
    ${PROJECT_NAME}               # Install the output library

  # TO:
    RUNTIME DESTINATION bin
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib
)

###################################################
#           Exports
###################################################
pluginlib_export_plugin_description_file(hardware_interface plugins.xml)
ament_export_include_directories(
  include
)
ament_export_libraries(
  ${PROJECT_NAME}
)
ament_export_dependencies(
  hardware_interface
  pluginlib
  rclcpp

  my_nested_interface_package
)

ament_package()

The MyNestedInterface class: (parameter to the nested ClassLoader template above)

namespace my_nested_interface_package {
class MyNestedInterface {
public:
    virtual void do_something() = 0;
};
} // namespace

The CMakeLists.txt for my_nested_interface_package: (It is built via cmake as an INTERFACE target)

cmake_minimum_required(VERSION 3.8)
project(my_nested_interface_package)

###################################################
#           Compilation Flags/Options
###################################################
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

###################################################
#           Package dependencies
###################################################
find_package(ament_cmake REQUIRED)
find_package(ament_cmake_core REQUIRED)
find_package(ament_index_cpp REQUIRED)
find_package(pluginlib REQUIRED)
find_package(rclcpp REQUIRED)

###################################################
#           Libraries
###################################################
add_library(${PROJECT_NAME ...
(more)
edit retag flag offensive close merge delete