LibraryLoadException when instantiating nested ClassLoader within custom SystemInterface implementation
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:
- 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. - If I include the PROBLEMATIC-CODE-HERE section (and recompile), I get the
LibraryLoadException
for the entiremy_hardware_package
(so, even the log-statements-here section doesn't get printed). Merely adding that statement makes themy_hardware_package
unloadable, with theLibraryLoadException
. - 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 noLibraryLoadException
.
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 ...