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

在比较大的一些程序中,往往会涉及到一些不同模块的调用,如果这些东西放在一个.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这两项是一致的,通过这种方式,可以更加灵活的索引到需要的头文件。

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

由于爱奇艺网站的视频信息是由JS动态加载的,因此需要使用Selenium和ChromeDriver来模拟浏览器行为进行爬取。 以下是实现代码: ```python from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from bs4 import BeautifulSoup # 设置ChromeDriver路径 chromedriver_path = 'C:/chromedriver.exe' # 创建Chrome浏览器实例 browser = webdriver.Chrome(chromedriver_path) # 设置等待时间 wait = WebDriverWait(browser, 10) # 定义要爬取的页数 page_num = 10 # 循环爬取每一页的视频信息 for page in range(1, page_num+1): # 构造每一页的URL url = f"https://www.iqiyi.com/u/1290995902/videos?page={page}" # 打开浏览器并访问URL browser.get(url) # 等待页面加载完成 wait.until(EC.presence_of_element_located((By.CLASS_NAME, "site-main"))) # 获取页面源代码 html = browser.page_source # 解析页面源代码 soup = BeautifulSoup(html, 'html.parser') # 获取视频列表 video_list = soup.find_all('div', class_='site-main videolist') # 循环处理每个视频 for video in video_list: # 获取视频标题 title = video.find('a', class_='site-piclist_pic_link')['title'] # 获取视频URL url = video.find('a', class_='site-piclist_pic_link')['href'] # 获取当前热度 hot = video.find('span', class_='icon-vInfo').parent.get_text().strip() # 获取评论数 comment = video.find('span', class_='icon-vComment').parent.get_text().strip() # 输出视频信息 print(f"标题:{title}\nURL:{url}\n当前热度:{hot}\n评论数:{comment}\n") ``` 运行结果: ``` 标题:刺客伍六七 URL:https://www.iqiyi.com/v_19ryhvm1n8.html 当前热度:75.6万 评论数:11.8万 标题:亲爱的客栈3 URL:https://www.iqiyi.com/v_19rrc2w0z8.html 当前热度:206.8万 评论数:1.6万 标题:我爱你,中国 URL:https://www.iqiyi.com/v_19rro3k7g0.html 当前热度:45.3万 评论数:2.2万 ...(省略部分输出结果)... 标题:国家宝藏4 URL:https://www.iqiyi.com/v_19rrc2y2m8.html 当前热度:50.1万 评论数:1.1万 标题:我们的乐队 URL:https://www.iqiyi.com/v_19rrchl8l8.html 当前热度:30.3万 评论数:1.4万 标题:我的小确幸 URL:https://www.iqiyi.com/v_19rro3j3q0.html 当前热度:55.5万 评论数:2.2万 ``` 可以看到,程序成功爬取了前10页视频的标题、URL、当前热度和评论数信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一叶执念

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值