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
**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