ROS中不同文件之间的引用小结

news2024/12/26 11:58:04

在比较大的一些程序中,往往会涉及到一些不同模块的调用,如果这些东西放在一个.cpp文件内,这个文件会变的特别长,因此会使用多个文件互相引用。那么如何在ROS下进行这种不同文件下的引用呢,根据最近所学,简单记录一份笔记。

1、头文件的引用

头文件的引用可以说是最常见的,很多时候都会用到,举个简单的例子:

在multiple_file_test/include/multiple_file_test/test1目录下建立一个.h文件名为file1.h:


#ifndef FILE1_H_
#define FILE1_H_

#include <memory>
namespace multiple_file_test {
namespace file1 {
class Test1 {
 public:
  //1、关键字 explicit 用于声明构造函数,以防止隐式类型转换和复制(或赋值)构造函数的自动创建。
  //错误方式:MyClass myObject = 10; // 错误:不允许隐式转换
  //正确方式:MyClass myObject(10); // 正确
  //2、禁用复制构造函数和赋值操作符:在 Test1 类的示例中,复制构造函数和赋值操作符被删除(使用 = delete),这意味着编译器不会为这个类自动生成默认的复制构造函数和赋值操作符。
  explicit Test1();
  ~Test1();

  Test1(const Test1 &) = delete;
  Test1 &operator=(const Test1 &) = delete;
 private:
};
int function1(){
    return 123;
}
}
}

#endif

上述代码是一个最简单的头文件形式,它定义了一个namespace以及一个函数function1,这个函数会返回一个int型的数字123。

然后,在src目录下,建立一个main函数调用这个函数:

#include "ros/ros.h"

#include "multiple_file_test/test1/file1.h"
namespace multiple_file_test {
namespace {

void Run() {
  auto value = multiple_file_test::file1::function1();
  ROS_INFO("debug");
  std::cout<<"value:"<<value<<std::endl;
}
}
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "multiple_file_test");
    ROS_INFO("multiple_file_test_node start");
    multiple_file_test::Run();
    return 0;
}

这里首先引入了头文件file1.h,然后在工作空间内调用了这个头文件下的具体函数function1,并打印最终结果。接下来编译这个头文件,在cmakelist中添加以下内容:

add_executable(main src/main.cpp)
add_dependencies(main ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})  
target_link_libraries(main ${CERES_LIBRARIES} ${catkin_LIBRARIES}) 

然后编译

然后

报错了:

fatal error: multiple_file_test/test1/file1.h: 没有那个文件或目录
    3 | #include "multiple_file_test/test1/file1.h"
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.

上面确实是有这个文件的,但是编译的时候没有找到,所以这时候需要在cmakelist中添加头文件所在路径:

include_directories(
  ${PROJECT_SOURCE_DIR}/include
  ${catkin_INCLUDE_DIRS}
)

重新编译后就可以执行了,然后运行可以得到如下结果:
在这里插入图片描述通过这种方式,可以实现在一个main函数中引用同一文件夹下的其他头文件了。

2、.cc函数的引用

除了引用某些头文件中的函数外,更多时候还需要引用其他.cc或者.cpp文件下的内容,举个例子,首先在multiple_file_test/include/multiple_file_test/test2目录下建立一个.h文件名为file2.h:


#ifndef FILE2_H_
#define FILE2_H_

#include <memory>
namespace multiple_file_test {
namespace file2 {
class Test1 {
 public:
  //1、关键字 explicit 用于声明构造函数,以防止隐式类型转换和复制(或赋值)构造函数的自动创建。
  //错误方式:MyClass myObject = 10; // 错误:不允许隐式转换
  //正确方式:MyClass myObject(10); // 正确
  //2、禁用复制构造函数和赋值操作符:在 MapBuilder 类的示例中,复制构造函数和赋值操作符被删除(使用 = delete),这意味着编译器不会为这个类自动生成默认的复制构造函数和赋值操作符。
  explicit Test1();
  ~Test1();

  Test1(const Test1 &) = delete;
  Test1 &operator=(const Test1 &) = delete;
 private:

  
};

int function1();

}
}

#endif

这个代码本身与file1.h的代码基本是一致的,内容上的区别在于这里的函数function1只进行了声明但是没有函数实现。然后在同一目录下再建立一个.cc文件进行函数实现:

#include "multiple_file_test/test2/file2.h"
namespace multiple_file_test {
namespace file2 {

int function1(){
  return 0;
}
}  
}  

这个文件补充了上面.h文件中的函数实现。同样再使用main函数调用这个函数:

#include "ros/ros.h"

#include "multiple_file_test/test1/file1.h"
#include "multiple_file_test/test2/file2.h"
namespace multiple_file_test {
namespace {

void Run() {
  auto value = multiple_file_test::file1::function1();
  ROS_INFO("debug");
  std::cout<<"value:"<<value<<std::endl;
  int value2 = multiple_file_test::file2::function1();
  ROS_INFO("debug");
  std::cout<<"value2:"<<value2<<std::endl;
}

}
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "multiple_file_test");
    ROS_INFO("multiple_file_test_node start");
    multiple_file_test::Run();
    return 0;
}

接下来进行编译,但是此时会报错:

CMakeFiles/main.dir/src/main.cpp.o: in function `multiple_file_test::(anonymous namespace)::Run()':
main.cpp:(.text+0x18e): undefined reference to `multiple_file_test::file2::function1()'
collect2: error: ld returned 1 exit status

这是由于主函数编译的时候只找到了头文件但是没有找到具体的函数实现。如果要找到函数实现,可以通过下述方式修改Cmakelist文件增加.cc的检索路径:

方式一、指定路径
在Cmakelist中给定确定路径,针对文件夹目录比较单一的情况下使用,例如:


file(GLOB MULTIPLE_FILE_SOURCES "include/multiple_file_test/test2/*.h" "include/multiple_file_test/test2/*.cc")
add_library(multiple_file_lib ${MULTIPLE_FILE_SOURCES})
add_executable(main src/main.cpp)
add_dependencies(main ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})  
target_link_libraries(main ${CERES_LIBRARIES} ${catkin_LIBRARIES} multiple_file_lib)

这个地方一共涉及了三个更改,首先是增加了file与add_library,然后是在target_link_libraries中添加了library。它的特点是file中路径必须是给定完整的,如果有多个目录的话写起来就会比较冗余。

方式二、检索路径
为了弥补上面的问题,可以采用路径检索的形式让其自动搜索。这里可以有两种不同的写法:

file(GLOB_RECURSE MULTIPLE_FILE_SOURCES
  "${CMAKE_SOURCE_DIR}/multiple_file_test/*.h"
  "${CMAKE_SOURCE_DIR}/multiple_file_test/*.cc")

或者:

file(GLOB_RECURSE MULTIPLE_FILE_SOURCES
  "${CMAKE_CURRENT_SOURCE_DIR}/include/*.h"
  "${CMAKE_CURRENT_SOURCE_DIR}/include/*.cc")

这两者的区别主要在于CMAKE_SOURCE_DIR与CMAKE_CURRENT_SOURCE_DIR。CMAKE_SOURCE_DIR与CMAKE_CURRENT_SOURCE_DIR是CMake 中两个非常重要的变量,它们定义了源代码树中的两个不同的目录,具体区别如下:

CMAKE_SOURCE_DIR:
    这个变量指向包含顶级 CMakeLists.txt 文件的目录。
    它是项目源代码的根目录,即你调用 cmake 命令的目录。
    无论在项目中的哪个 CMakeLists.txt 文件中,CMAKE_SOURCE_DIR 总是指向同一个目录。

CMAKE_CURRENT_SOURCE_DIR:
    这个变量指向当前正在处理的 CMakeLists.txt 文件所在的目录。
    当你在项目的某个子目录中调用 cmake 或处理子目录中的 CMakeLists.txt 文件时,CMAKE_CURRENT_SOURCE_DIR 会根据当前文件的位置而变化。
    它是相对的,并且每个子目录中的 CMakeLists.txt 文件都会有不同的值。

简单来说,CMAKE_SOURCE_DIR 是项目的根目录,而 CMAKE_CURRENT_SOURCE_DIR 是当前正在被处理的目录。

举个例子:当你需要引用项目根目录下的文件或目录时,可以使用 CMAKE_SOURCE_DIR。例如,如果你的 CMakeLists.txt 位于 src/ 目录下,而你需要包含位于项目根目录下的 config/ 目录中的文件,你可以这样做:

include_directories(${CMAKE_SOURCE_DIR}/config)

而当你需要引用当前 CMakeLists.txt 所在目录下的文件或目录时,可以使用 CMAKE_CURRENT_SOURCE_DIR。例如,如果你正在 src/ 目录下的 CMakeLists.txt 中,并且需要包含相邻 include/ 目录中的文件,你可以这样做:

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)

后续的add_library,以及在target_link_libraries中添加了library这两项是一致的,通过这种方式,可以更加灵活的索引到需要的头文件。

然后编译运行一下,可以得到输出的结果为:
在这里插入图片描述
可以看到其调用了两个不同的工作空间下的函数并分别得到了不同的结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1907636.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【大模型LLM面试合集】大语言模型基础_Word2Vec

Word2Vec 文章来源&#xff1a;Word2Vec详解 - 知乎 (zhihu.com) 1.Word2Vec概述 Word2Vec是google在2013年推出的一个NLP工具&#xff0c;它的特点是能够将单词转化为向量来表示&#xff0c;这样词与词之间就可以定量的去度量他们之间的关系&#xff0c;挖掘词之间的联系。 …

世界商用飞机机型大全-使用Java抓取FlightAware后的答案

目录 前言 一、数据说明 1、实时航班飞机机型数据 2、网页结构分析 二、使用Java进行信息抓取 1、定义页面PageVO对象 2、爬取属性定义 3、启动信息抓取组件 三、成果分析 1、商业飞行的飞机机型的种类 2、飞机种类排名前十名 3、航班数排名后十名 4、看中国国产大飞…

解读BASE理论:高可用性与性能的完美平衡

Base概念 BASE 理论是一种处理大规模分布式系统中的数据一致性问题的思路。相比于传统的严格一致性&#xff0c;它更灵活&#xff0c;适用于那些需要高可用性和性能的系统。BASE 理论由三个部分组成&#xff1a; 基本可用&#xff08;Basically Available&#xff09; 基本可用…

《Programming from the Ground Up》阅读笔记:p19-p48

《Programming from the Ground Up》学习第2天&#xff0c;p19-p48总结&#xff0c;总计30页。 一、技术总结 1.object file p20, An object file is code that is in the machine’s language, but has not been completely put together。 之前在很多地方都看到object fi…

算法期末程序填空

1.有重复元素全排列的计数问题(部分正确 【考察知识点】有重复元素的全排列的计数 【题目描述】 共有n个小球&#xff08;1<n<20&#xff09;&#xff0c;这n个小球有k种颜色&#xff08;1<k<10&#xff09;&#xff0c;白色s1​个&#xff0c;红色s2​个&#…

大模型时代的蓝海任务,GPT4V准确率不足10%,港科大发布指代理解基准RefCOCO

谈到多模态大模型的应用场景&#xff0c;除了生成任务以外&#xff0c;应用最广泛的可能就是在图像和视频中进行目标检测。 目标检测要求从图像中识别并标注出所有感兴趣的对象&#xff0c;并给每个对象分配一个类别标签。典型的目标检测方法会生成边界框&#xff0c;标记出图…

百度、谷歌、必应收录个人博客网站

主要是给各个搜索引擎提交你的sitemap文件&#xff0c;让别人能搜到你博客的内容。 主题使用的Butterfly。 生成sitemap 安装自动生成sitemap插件。 npm install hexo-generator-sitemap --save npm install hexo-generator-baidu-sitemap --save在站点配置文件_config.yml…

LabVIEW高能质子束流密度分布测试系统

LabVIEW平台开发的高能质子束流密度分布测试系统。该系统主要应用于电子器件的抗辐射加固试验&#xff0c;旨在精确测量高能质子束的密度分布&#xff0c;以评估电子器件在辐射环境下的性能表现和耐受能力。 系统组成与设计 硬件组成&#xff1a; 法拉第杯探测器&#xff1a;…

C++ 类和对象 拷贝构造函数

一 拷贝构造函数的概念&#xff1a; 拷贝构造函数是一种特殊的构造函数&#xff0c;用于创建一个对象是另一个对象的副本。当需要用一个已存在的对象来初始化一个新对象时&#xff0c;或者将对象传递给函数或从函数返回对象时&#xff0c;会调用拷贝构造函数。 二 拷贝构造函…

静态路由配置注意事项及黑洞路由的使用

静态路由 1 . 定义 从管理员处学习到的数据转发路径&#xff0c;就称为静态路由。 2 . 路由表 Proto &#xff1a;协议&#xff08; Protocol &#xff09; Direct — 直连链路Static — 静态路由RIP 、OSPF 等 — 动态路由 Pre : 优先级&#xff08; Preference &#x…

ozon跨境电商可以做吗,俄罗斯ozon跨境电商可不可以做

当今全球化的浪潮下&#xff0c;跨境电商已成为连接世界各地消费者与商家的桥梁&#xff0c;为无数企业开辟了全新的市场蓝海。俄罗斯&#xff0c;作为世界上最大的国家之一&#xff0c;其电商市场近年来蓬勃发展&#xff0c;尤其是ozon平台&#xff0c;作为俄罗斯本土的电商巨…

你真的会信息收集嘛,4k字渗透测试信息收集10大技巧

前言 在渗透测试中&#xff0c;信息收集是非常关键的一步&#xff0c;它为后续的漏洞发现和利用提供了重要的基础。以下是非常详细的信息收集方式&#xff1a; 一、被动信息收集 被动信息收集是指在不与目标系统直接交互的情况下&#xff0c;通过公开渠道获取目标系统的相关…

Redhat 安装 docker 网络连接超时问题

目录 添加阿里云的Docker CE仓库 更新YUM缓存 安装 Docker Engine 启动并设置Docker自启动 验证 Docker 安装 [userlocalhost ~]$ sudo yum-config-manager --add-repohttps://download.docker.com/linux/centos/docker-ce.repo 正在更新 Subscription Management 软件仓库…

高考志愿填报的六个不要

在高考志愿填报这个关键时刻&#xff0c;确实需要谨慎行事&#xff0c;避免一些常见的错误。以下是高考志愿填报的六个“不要”&#xff0c;希望能为你提供一些有用的建议&#xff1a; 1、不要盲目跟风 每个人的兴趣、能力和未来规划都不同&#xff0c;不要仅仅因为某个专业或…

202406 CCF-GESP Python 三级试题及详细答案注释

202406 CCF-GESP Python 三级试题及详细答案注释 1 单选题(每题 2 分,共 30 分)第 1 题 小杨父母带他到某培训机构给他报名参加CCF组织的GESP认证考试的第1级,那他可以选择的认证语言有几种?( ) A. 1 B. 2 C. 3 D. 4答案:C解析:目前CCF组织的GESP认证考试有C++、Pyth…

LVS+Nginx高可用集群---Nginx进阶与实战

1.Nginx中解决跨域问题 两个站点的域名不一样&#xff0c;就会有一个跨域问题。 跨域问题&#xff1a;了解同源策略&#xff1a;协议&#xff0c;域名&#xff0c;端口号都相同&#xff0c;只要有一个不相同那么就是非同源。 CORS全称Cross-Origin Resource Sharing&#xff…

Vue+ElementUi实现录音播放上传及处理getUserMedia报错问题

1.Vue安装插件 npm install --registryhttps://registry.npmmirror.com 2.Vue页面使用 <template><div class"app-container"><!-- header --><el-header class"procedureHeader" style"height: 20px;"><el-divid…

AJAX快速入门(一) express框架的安装和使用范例

主打一个有用 首先保证安装了nodejs环境 打开终端 初始化npm npm init安装express npm i express测试样例 目录结构 样例代码 express.js //引入express const express require(express);//创建应用对象 const app express();//创建路由规则 //req是请求对象&#x…

C#委托事件的实现

1、事件 在C#中事件是一种特殊的委托类型&#xff0c;用于在对象之间提供一种基于观察者模式的通知机制。 1.1、事件的发送方定义了一个委托&#xff0c;委托类型的声明包含了事件的签名&#xff0c;即事件处理器方法的签名。 1.2、事件的订阅者可以通过运算符来注册事件处理器…

HTTP 请求走私漏洞详解

超详细的HTTP请求走私漏洞教程&#xff0c;看完还不会你来找我。 1. 简介 HTTP请求走私漏洞&#xff08;HTTP Request Smuggling&#xff09;发生在前端服务器&#xff08;也称代理服务器&#xff0c;一般会进行身份验证或访问控制&#xff09;和后端服务器在解析HTTP请求时&…