c++cmakeroscatkin

How to compile and link callback class (for ROS messages)


Essentially I have defined a class to handle my callbacks, with the declarations and definitions split between a header and source file. However I am having troubles compiling a file which then uses said callback (despite me attempted to link the object file of the callback), specifically I get a linker error:

[ 98%] Linking CXX executable /home/salih/documents/university/3/6001/code/devel/lib/sdr_ros/sdr_ros
/usr/bin/ld: CMakeFiles/sdr_ros.dir/src/sdr_ros.cpp.o: in function `main':
sdr_ros.cpp:(.text+0x41e): undefined reference to `sdr_ros::TravelInfoCallback::callback(boost::shared_ptr<sdr_ros::TravelInfo_<std::allocator<void> > const> const&)'
collect2: error: ld returned 1 exit status

Package is called sdr_ros & the message it's dealing with is called TravelInfo which is stored in the msg directory.

include/travel_info_callback.hpp (Header file):

#ifndef TRAVEL_INFO_CALLBACK_HPP
#define TRAVEL_INFO_CALLBACK_HPP
#pragma once

#include <tuple>

#include "sdr_ros/TravelInfo.h" // message itself

/**
  * @brief This file contains declarations for callback functionality which processes TravelInfo ROS message
  */

namespace sdr_ros {

    class TravelInfoCallback {
        /**
          * @brief TravelInfoCallback - class to store values returned by callback pertaining to distance / angles changes
          */
        private:
            float _delta_x, _delta_y, _delta_z ;

            float _yaw, _pitch, _roll ;

        public:
            /**
              * @brief TravelInfoCallback - constructor initialises class values
              */
            TravelInfoCallback() noexcept ;

            /**
              * @brief distance - method returning tuple of distance travelled along x, y, z
              * @return std::tuple<float, float, float> - value pertaining to distance travelled along x, y, z axis
              */
            ::std::tuple<float, float, float> distance() const noexcept ;

            /**
              * @brief heading - method returning tuple of euler angles yaw, pitch, roll
              * @return std::tuple<float, float, float> - value pertaining to distance travelled around the (yaw) z, (pitch) y, (roll) x axis
              */
            ::std::tuple<float, float, float> heading() const noexcept ;

            /**
              * @brief callback - method extracting distance travelled and heading angle from ROS message
              * @param const sdr_ros::TravelInfoConstPtr& - const reference to boost shared pointer to ROS message storing distance travelled and heading angle
              */
            void callback(const TravelInfoConstPtr&) noexcept ;

            // below are defaulted and deleted methods
            TravelInfoCallback(const TravelInfoCallback&) = delete ;
            TravelInfoCallback& operator=(const TravelInfoCallback&) = delete ;
            TravelInfoCallback(TravelInfoCallback&&) = delete ;
            TravelInfoCallback&& operator=(TravelInfoCallback&&) = delete ;
            ~TravelInfoCallback() noexcept = default ;
    } ;

}

#endif // TRAVEL_INFO_HPP

src/travel_info.cpp

#include <tuple>

#include "sdr_ros/TravelInfo.h" // message itself

#include "travel_info_callback.hpp"

/**
  * @brief This file contains defintions for callback functionality which processes travel_info ROS message
  */

sdr_ros::TravelInfoCallback::TravelInfoCallback() noexcept : _delta_x(0.f), _delta_y(0.f), _delta_z(0.f), _yaw(0.f), _pitch(0.f), _roll(0.f) {} ;

std::tuple<float, float, float> sdr_ros::TravelInfoCallback::distance() const noexcept
{
    return std::tuple<float, float, float>{
        this->_delta_x, this->_delta_y, this->_delta_z
    } ;
}

std::tuple<float, float, float> sdr_ros::TravelInfoCallback::heading() const noexcept
{
    return std::tuple<float, float, float>{
        this->_yaw, this->_pitch, this->_roll
    } ;
}

inline void sdr_ros::TravelInfoCallback::callback(const sdr_ros::TravelInfoConstPtr& travel_info) noexcept
{
    this->_delta_x = travel_info->delta_x ;
    this->_delta_y = travel_info->delta_y ;
    this->_delta_z = travel_info->delta_z ;
    this->_yaw = travel_info->yaw ;
    this->_pitch = travel_info->pitch ;
    this->_roll = travel_info->roll ;
}

src/sdr_ros (source code using callback)

#include <ros/ros.h>
#include <ros/console.h>
#include <ros/callback_queue.h>

#include <string>
#include <cmath>

#include <geometry_msgs/Pose.h>

#include "sdr_ros/TravelInfo.h"

#include "preprocessing.hpp"
#include "pose.hpp"
#include "travel_info_callback.hpp"

/**
  * @brief This file serves as the ROS node which reads from the network and calls relevant functionality to calculate and distribute pose information
  */

int main(int argc, char** argv)
{
    /* Initialisation */
    const std::string node_name = "sdr_ros" ;
    ros::init(argc, argv, node_name); ROS_INFO_STREAM(node_name << " running") ; // initialise ROS node
    ros::NodeHandle nh("~") ; // handle to ROS communications (topics, services)

    sdr::Pose pose ;
    std::string initial_pose_file ;
    if(nh.getParam("initial_pose_file", initial_pose_file))
    {
        pose = sdr::extract_initial_pose(initial_pose_file) ;
    }

    sdr_ros::TravelInfoCallback cb ; // callback object with methods
    ros::Subscriber sub = nh.subscribe<sdr_ros::TravelInfo>("/travel_info", 100, &sdr_ros::TravelInfoCallback::callback, &cb) ; // subscribe to topic distance_info

    geometry_msgs::Pose pose_msg ;
    ros::Publisher pub = nh.advertise<geometry_msgs::Pose>("/odometry", 1) ; // publishes information about where car as XYZ, orientation as quaternion

    /* Main functionality */
    while(ros::ok()) // while ROS is running - the actual useful computation
    {
        /* Get info */
        if(ros::getGlobalCallbackQueue()->callOne() == ros::CallbackQueue::CallOneResult::Empty) continue ;
        // ^ we can't simply use `ros::spinOnce` as there's no indication of an empty queue, meaning we'd be processing the same message multiple times, exponentially increasing predicted distance.

        const auto [delta_x, delta_y, delta_z] = cb.distance() ;
        if(!delta_x && !delta_y && !delta_z)
        {
            continue ; // no point processing odom when we haven't moved
        }
        const auto [yaw, pitch, roll] = cb.heading() ;

        pose.update_position(delta_x, delta_y, delta_z) ;
        pose.update_orientation(yaw, pitch, roll) ;

        pose_msg.position.x = pose.position()(0,0) ;
        pose_msg.position.y = pose.position()(0,1) ;
        pose_msg.position.z = pose.position()(0,2) ;
        pose_msg.orientation.x = pose.orientation().x() ;
        pose_msg.orientation.y = pose.orientation().y() ;
        pose_msg.orientation.z = pose.orientation().z() ;
        pose_msg.orientation.w = pose.orientation().w() ;

        ROS_INFO_STREAM("Current position: " << pose_msg.position) ;
        ROS_INFO_STREAM("Current orientation: " << pose_msg.orientation << '\n') ;

        /* Publish odometry */
        pub.publish(pose_msg) ;
    }

    /* E(nd) O(f) P(rogram) */
    return 0 ;
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.2)
project(sdr_ros)

## Compile as C++20, supported in ROS Kinetic and newer
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_CXX_FLAGS "-Wall -Wextra")
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O0")

find_package(Eigen3 REQUIRED)

################################################
##    Find catkin macros and libraries        ##
################################################
find_package(catkin REQUIRED COMPONENTS
    roscpp
    rospy
    std_msgs
    tf
    nav_msgs
    genmsg
    message_generation
)

## System dependencies are found with CMake's conventions
# find_package(Boost REQUIRED COMPONENTS system)

################################################
## Declare ROS messages, services and actions ##
################################################

## Generate messages in the 'msg' folder
add_message_files(
    DIRECTORY
        msg
    FILES
        TravelInfo.msg
)

## Generate services in the 'srv' folder
#add_service_files(
#    FILES
#)

## Generate added messages and services with any dependencies listed here
generate_messages(
    DEPENDENCIES
        std_msgs
        geometry_msgs
)

###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if your package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package(
#    INCLUDE_DIRS
#        include
#        ${EIGEN3_INCLUDE_DIR}
#        libraries/sdr/include/
    CATKIN_DEPENDS
        message_runtime
)

###########
## Build ##
###########

add_subdirectory(libraries/sdr)

## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
    include
    libraries/sdr/include/
    ${EIGEN3_INCLUDE_DIR}
    ${catkin_INCLUDE_DIRS}
)

## Declare a C++ executable
add_library(travel_info_callback ${CMAKE_CURRENT_SOURCE_DIR}/src/travel_info_callback.cpp)
target_include_directories(travel_info_callback PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(travel_info_callback ${catkin_LIBRARIES})
add_dependencies(travel_info_callback sdr_ros_generate_messages_cpp)

add_executable(sdr_ros src/sdr_ros.cpp)
target_link_libraries(sdr_ros ${catkin_LIBRARIES} travel_info_callback detailed_exception.o preprocessing.o pose.o yaml-cpp)
add_dependencies(sdr_ros sdr_ros_generate_messages_cpp)

I hope I've given enough information and that someone can help. Much appreciated


Solution

  • **inline** void sdr_ros::TravelInfoCallback::callback(const sdr_ros::TravelInfoConstPtr& travel_info) noexcept
    

    I accidentally left the inline keyword within the function header. Removing it allowed for a proper function declaration which resolved all my issues