Background: I work in underwater robotics, and DCCL is a library for encoding of messages to fit within our extremely constrained message sizes based on an original protobuf format. Just for practice before applying this to one of our real messages, I'm modifying the basic publisher/subscriber from the tutorial to encode the message to DCCL before sending. It seems to do just fine with the protobuf library, but I'm getting an undefined reference error on the DCCL library, which I assuming is a linking error.
Error:
teddybouch@Norby:~/workspace/ros2_ws$ colcon build --packages-select cpp_pubsub
Starting >>> cpp_pubsub
--- stderr: cpp_pubsub
/usr/bin/ld: CMakeFiles/talker.dir/src/publisher_member_function.cpp.o: in function `MinimalPublisher::timer_callback()':
publisher_member_function.cpp:(.text._ZN16MinimalPublisher14timer_callbackEv[_ZN16MinimalPublisher14timer_callbackEv]+0xa2): undefined reference to `dccl::Codec::Codec(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
/usr/bin/ld: publisher_member_function.cpp:(.text._ZN16MinimalPublisher14timer_callbackEv[_ZN16MinimalPublisher14timer_callbackEv]+0xed): undefined reference to `testdccl::NavigationReport::NavigationReport()'
/usr/bin/ld: publisher_member_function.cpp:(.text._ZN16MinimalPublisher14timer_callbackEv[_ZN16MinimalPublisher14timer_callbackEv]+0x1d5): undefined reference to `dccl::Codec::encode(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, google::protobuf::Message const&, bool, int)'
/usr/bin/ld: publisher_member_function.cpp:(.text._ZN16MinimalPublisher14timer_callbackEv[_ZN16MinimalPublisher14timer_callbackEv]+0x3b7): undefined reference to `testdccl::NavigationReport::~NavigationReport()'
/usr/bin/ld: publisher_member_function.cpp:(.text._ZN16MinimalPublisher14timer_callbackEv[_ZN16MinimalPublisher14timer_callbackEv]+0x3c6): undefined reference to `dccl::Codec::~Codec()'
/usr/bin/ld: publisher_member_function.cpp:(.text._ZN16MinimalPublisher14timer_callbackEv[_ZN16MinimalPublisher14timer_callbackEv]+0x48b): undefined reference to `testdccl::NavigationReport::~NavigationReport()'
/usr/bin/ld: publisher_member_function.cpp:(.text._ZN16MinimalPublisher14timer_callbackEv[_ZN16MinimalPublisher14timer_callbackEv]+0x4a3): undefined reference to `dccl::Codec::~Codec()'
/usr/bin/ld: CMakeFiles/talker.dir/src/publisher_member_function.cpp.o: in function `void dccl::Codec::load<testdccl::NavigationReport>()':
publisher_member_function.cpp:(.text._ZN4dccl5Codec4loadIN8testdccl16NavigationReportEEEvv[_ZN4dccl5Codec4loadIN8testdccl16NavigationReportEEEvv]+0x11): undefined reference to `testdccl::NavigationReport::descriptor()'
/usr/bin/ld: publisher_member_function.cpp:(.text._ZN4dccl5Codec4loadIN8testdccl16NavigationReportEEEvv[_ZN4dccl5Codec4loadIN8testdccl16NavigationReportEEEvv]+0x28): undefined reference to `dccl::Codec::load(google::protobuf::Descriptor const*, int)'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/talker.dir/build.make:132: talker] Error 1
make[1]: *** [CMakeFiles/Makefile2:82: CMakeFiles/talker.dir/all] Error 2
make: *** [Makefile:141: all] Error 2
---
Failed <<< cpp_pubsub [0.97s, exited with code 2]
Summary: 0 packages finished [1.16s]
1 package failed: cpp_pubsub
1 package had stderr output: cpp_pubsub
Except for the addition of the proto directory and the protobuf message definition file in it, I think that the only relevant changes are in the CMakeLists.txt and publisher_member_function.cpp, which I'm including below, but if I've missed something or anyone wants all the code for some reason, the whole package is on my Dropbox here.
CMakeLists.txt:
cmake_minimum_required(VERSION 3.5)
project(cpp_pubsub)
# Default to C99
if(NOT CMAKE_C_STANDARD)
set(CMAKE_C_STANDARD 99)
endif()
# Default to C++14
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()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(dccl REQUIRED)
message(STATUS "Using DCCL in ${DCCL_DIR}")
find_package(Protobuf REQUIRED)
if(NOT DEFINED DCCL_INCLUDE_DIR)
#for DCCL 3.0.3 and newer
get_target_property(DCCL_INCLUDE_DIR dccl INTERFACE_INCLUDE_DIRECTORIES)
endif()
if(DCCL_INCLUDE_DIR)
message(STATUS "\tIncluding DCCL protobuf dir: ${DCCL_INCLUDE_DIR}")
include_directories("${DCCL_INCLUDE_DIR}")
#protobuf_include_dirs("${DCCL_INCLUDE_DIR}")
endif()
# build the protobuf messages
file(GLOB ProtoFiles "${CMAKE_CURRENT_SOURCE_DIR}/proto/*.proto")
PROTOBUF_GENERATE_CPP(ProtoSources ProtoHeaders ${ProtoFiles})
add_library(proto STATIC ${ProtoSources} ${ProtoHeaders})
target_link_libraries(proto ${PROTOBUF_LIBRARY})
message( STATUS ${CMAKE_BINARY_DIR} )
include_directories( ${CMAKE_BINARY_DIR} )
add_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp std_msgs dccl Protobuf)
add_executable(listener src/subscriber_member_function.cpp)
ament_target_dependencies(listener rclcpp std_msgs)
install(TARGETS
talker
listener
DESTINATION lib/${PROJECT_NAME})
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
publisher_member_function.cpp:
#include <chrono>
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
#include "dccl.h"
#include "navreport.pb.h"
using namespace std::chrono_literals;
/* This example creates a subclass of Node and uses std::bind() to register a
* member function as a callback from the timer. */
class MinimalPublisher : public rclcpp::Node
{
public:
MinimalPublisher()
: Node("minimal_publisher"), count_(0)
{
publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);
timer_ = this->create_wall_timer(
500ms, std::bind(&MinimalPublisher::timer_callback, this));
}
private:
void timer_callback()
{
auto message = std_msgs::msg::String();
std::string encoded_bytes;
dccl::Codec codec;
// Create the DCCL-encoded protobuf message
codec.load<testdccl::NavigationReport>();
testdccl::NavigationReport report;
report.set_x( count_++ );
report.set_y( 0 - count_ );
report.set_z( -999 );
//report.set_veh_class( testdccl::NavigationReport::AUV );
report.set_battery_ok( true );
codec.encode( &encoded_bytes, report );
// Serialize the protobuf data
message.data = encoded_bytes;
RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
publisher_->publish(message);
}
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
size_t count_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalPublisher>());
rclcpp::shutdown();
return 0;
}
Truth be told I'm still getting the hang of CMake and linking in general, so I think that what's going on is that I'm getting confused with the intersection of ROS, colcon, protobuf, DCCL, and CMake and I'm probably missing something obvious, but I've spent over an hour now looking between the CMake and DCCL documentation (plus various other Googling) to no avail, so if anyone has a moment to spare to look at it I would be grateful. Thanks!
- gcc version: 9.4.0
- CMake version: 3.16.3
- ROS version: foxy
Originally posted by teddybouch on ROS Answers with karma: 320 on 2022-05-06
Post score: 0