自动驾驶工具工作记录
C++
issues
issue 1
error LINK2001: 无法解析的外部符号public: static struct QMetaObject const DataHandlerBase::staticMetaObject
原因是自定义的链接库没有导出相关类或者函数
sregex_token_iterator
的特性
string
转数字
关于时间的函数和结构体
time
函数
1 |
|
得到自 1970-1-1 00:00:00 以来经过的秒数,结果可以通过返回值,也可以通过参数得到
1 |
|
localtime
1 |
|
用来获取系统时间,精度为秒;将时间(秒)数值变换成本地时间,考虑到本地时区和夏令时标志
1 |
|
localtime_r
1 |
|
用来获取系统时间,运行于 linux 平台下
1 |
|
localtime_s
用来获取系统时间,运行于 windows 平台下,与 localtime_r
只有参数顺序不一样
1 |
|
localtime
和 localtime_r
二者区别
localtime
对于多线程不安全,因为localtime
在使用时,只需定义一个指针,申请空间的动作由函数自己完成,这样在多线程的情况下,如果有另一个线程调用了这个函数,那么指针指向的 struct tm
结构体的数据就会改变- 在 l
ocaltime_s
与localtime_r
调用时,定义的是struct tm
的结构体,获取到的时间已经保存在struct tm
中,并不会受其他线程的影响
gettimeofday
1 |
|
获取秒、微秒、时区等信息;其参数 tv
是保存获取时间结果的结构体,参数 tz
用于保存时区结果,timezone
参数若不使用则传入 NULL
即可
1 |
|
它获得的时间精确到微秒 1e-6s
量级,在一段代码前后分别使用 gettimeofday
可以计算代码执行时间
linux 下 sprintf_s
函数的替代
windows 平台下线程安全的格式化字符串函数sprint_s
并非标准 C 函数,因此 linux 下无法使用,但可以使用 snprintf
函数代替
1 |
|
incomplete type xxx
used in nested name specifier
Error: incomplete type ‘QTime’ used in nested name specifier
声明 / 定义了 QTime
类型的变量,但是没有引入相应的头文件,即#include <QTime>
关于 filesystem
在 C++ 11、14、17 中的使用问题
- 在 C++ 11 时,该类库定义在
std::tr2::sys
命名空间 - C++ 14 中已经不存在
tr2
命名空间filesystem
放在std::experimental::filesystem
空间- 在(VS2019 以上)编译时可能会报错 C1189,提示该头文件已被取代,建议使用新的如果不想根据提示改用 C++ 17,那么我们可以坚持自己的想法,在项目配置中加预处理宏定义即可
_SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
- 在(VS2019 以上)编译时可能会报错 C1189,提示该头文件已被取代,建议使用新的如果不想根据提示改用 C++ 17,那么我们可以坚持自己的想法,在项目配置中加预处理宏定义即可
- 不同的编译环境可以使用宏定义区分
1
2
3
4
5#if (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L || __cplusplus >= 201703L)
namespace fs = std::filesystem
#else
namespace fs = std::experimental::filesystem;
#endif
‘filesystem’ in namespace ‘std’ does not name a type 出现这个报错可能是因为写错了
using fs = std::filesystem
和namespace fs = std::filesystem
Windows 下 gb2312 编码的源文件,在 Linux 下编译报错
- error: converting to execution character set: Invalid or incomplete multibyte or wide character
- error: converting UCN to execution character set: Invalid or incomplete multibyte or wide character
- failure to convert gbk to UTF-8
- c++ 源文件有多种编码格式
gcc 编译选项
-finput-charset
:输入字符集设置(需要和源文件编码一致),告诉编译器以什么样的编码形式读入源文件中的字符串-fexec-charset
:执行字符集设置(需要设置为当前运行环境支持的编码), 告诉编译器在内存中以什么样的编码形式保存字符串-fwide-exec-charset
:宽字符执行编码(在 windows 下应设置为 utf-16LE),告诉编译器在内存中以什么样的编码形式保存宽字符串
可能解决方法
尝试加上编码前缀
1 |
|
Visual Studio 无法打开编译器生成的文件:“xxx.obj”: Permission denied
删除其他配置平台编译后的文件,如 Debug 或 Relase 文件目录
不允许 dllimport
静态数据成员的定义
当要使用一个类的时候必须要有其定义,有两种方式:
引用其头文件,即
include "xxx.h"
使用导出类,即使用
__declspec(dllimport)
定义的类为导出类1
2
3
4class __declspec(dllimport) Test
{
}declspec(dllimport)
是 msvc 特有的描述符,如果是跨平台的情况这样写就会有问题如果确实需要使用
declspec(dllimport)
,msvc 规定:数据、静态数据成员和函数可以声明,但不能定义为dllimport
;对于普通的函数、类可行,然而对模板却不行,需要考虑编译器能不能支持对模板的分离式编译一个 .cpp 及其包括的所有 .h 文件编译后叫做一个编译单元,即 .obj 文件,然后由链接器把所有的 .obj 链接生成一个可执行文件;模板是需要具体化的,编译器知道碰到使用这个模板代码的时候才会把模板编译成二进制代码
__declspec(dllexport)
:声明一个导出函数,这个函数从 .dll 文件导出。一般用于 .dll 中省掉在 DEF 文件中手动定义导出哪些函数的一个方法;如果过在 .dll 中全是 c++ 的类的话,无法在 DEF 文件中指定导出的函数,这能用__declspec(dllexport)
导出类__declspec(dllimport)
:声明一个导入函数,这个函数是从别的 .dll 中导入,一般用于使用某个 .dll 的可执行文件中;不适用__declspec(dllimport)
也能正确编译代码,但是可以使编译器可以生成更好的代码;编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 .dll 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 .dll 边界的函数调用中。但是,必须使用__declspec(dllimport)
才能导入 .dll 中使用的变量
使用示例:
1 |
|
Windows 编程
WMIC 指令
windows 批处理 打开 exe 后关闭黑窗口
1 |
|
如果下面这样调用,需要等待程序窗口关闭之后窗黑口才会关闭的
xxx.exe exit
findstr
的使用
findstr
是一个在 Windows 系统的命令提示符或 PowerShell 中使用的命令行工具,主要用于在文本文件中搜索字符串。以下是一些关于findstr
的基本用法和选项:
基本字符串搜索
使用 findstr
可以在一个或多个文件中搜索包含特定字符串的行。例如,findstr "keyword" filename.txt
将在 filename.txt 文件中搜索包含 “keyword”
的行
findstr
支持使用正则表达式进行高级搜索。例如,findstr /r "^start" filename.txt
将匹配以 “start”
开头的行
/B
:在一行的开始配对模式/E
:在一行的结尾配对模式/L
:按字使用搜索字符串/R
:将搜索字符串作为正则表达式使用/S
:在当前目录和所有子目录中搜索匹配文件/I
:指定搜索不分大小写/X
:打印完全匹配的行/V
:只打印不包含匹配的行/N
:在匹配的每行前打印行数
搜索多个字符串
如果没有使用 /C
前缀,findstr 会使用空格分隔的搜索字符串。例如,findstr "hello there" x.y
会在文件 x.y 中寻找 “hello”
或“there”
.
:通配符,代表任何字符*
:重复,代表前一个字符或类出现零次或多次^
:行的开始$
:行的结束[class]
:字符类,代表任何在字符集中的字符[^class]
:补字符类,代表任何不在字符集中的字符[x-y]
:范围,代表在指定范围内的任何字符
Linux
从 windows 拷贝文件到 WSL 最快速的方法
- 在 WSL 用
sudo ls /mnt/*
列出系统所有的挂载盘,可以看到 windows 系统的所有盘都列出来了 - 然后使用
cp
命令
安装 .deb 文件
1 |
|
While
dpkg -i
indeed installs the package, it doesn’t do any automatic dependency resolution. Meanwhile there are two other alternative, usinggdebi
or theapt-get
tool
- 卸载 .deb
1 |
|
- 删除目录
1 |
|
Git
递归 git push/pull
在 Git 中,递归 git push/pull
操作是指在父模块中递归地操作子模块的 push 和 pull 操作。当一个代码库包含多个子模块时,可以使用递归 git push/pull
操作来同时对所有子模块进行相关操作,从而避免了手动逐个操作子模块的繁琐
递归git push
1 |
|
递归git pull
1 |
|
- 确保所有子模块都已经初始化和更新。如果有尚未初始化或更新的子模块,可以使用
git submodule init
和git submodule update
命令来初始化和更新子模块 - 如果在父模块中进行了修改,并且同时也在某个子模块中进行了修改,那么在递归
git push/pull
操作时可能会出现冲突。如果出现冲突,需要手动解决冲突并再次进行递归git push/pull
操作 - 在进行递归
git push/pull
操作时,确保所有子模块的远程仓库地址正确设置,并且拥有 push/pull 权限
如何删除 Git 中的未跟踪文件 untracked files
要删除所有未跟踪的文件,可以运行以下命令
1 |
|
如果想要删除未跟踪的文件和目录,可以使用 -d
参数
1 |
|
解决 Git 报 error unknown switch `e‘ 错误
在 VS Code 中,使用 git stash pop [<stash>]
语法报错
1 |
|
花括号在 PowerShell 中被认为是代码块执行标识符,若想正常使用,可用反引号 ` 进行转义
1 |
|
git stash 某个特定的文件
可以使用 git stash 命令结合 git checkout 和 git add 来实现
1 |
|
请注意,
-k
选项告诉 git stash 不要暂存那些已经被添加到暂存区的文件
git clone -depth 1 之后切换远程分支的方案
将 shallow clone 转换为 deep clone
1 |
|
git config 是容易被忽视的,shallow clone 之后,remote.origin.fetch 的值是 +refs/heads/master:refs/remotes/origin/master,所以会发现怎么 fetch 都没有其他分支
SSH-keygen 用法
https 和 SSH 的区别
- 前者可以随意克隆 github 上的项目,而不管是谁的;而后者则是你必须是你要克隆的项目的拥有者或管理员,且需要先添加 SSH key ,否则无法克隆
- https url 在 push 的时候是需要验证用户名和密码的;而 SSH 在 push 的时候,是不需要输入用户名的,如果配置 SSH key 的时候设置了密码,则需要输入密码的,否则直接是不需要输入密码的
在 github 上添加 SSH key 的步骤
1 |
|
CMake
相关问题
issue 1
The target name
“xxx.cpp”
is reserved or not valid for certain CMake featres, such as generator expressions, and may result in undefined behavior.
原始问题
1 |
|
由于 CMakeLists.txt 文件中没有定义 ${PROJECT}
变量,ADD_LIBRARY
少了一个参数导致报错
1 |
|
issue 2
- Cannot specify link libraries for target “PRIVATE” which is not built by this project.
- IMPORTED library can only be used with the INTERFACE keyword of target_link_libraries
原因同上,出现问题的语句:
1 |
|
issue 3
needed by xxx.so, not found (try using -rpath or -rpath-link)
没有将自己编译的动态库导入到 LD_LIBRARY_PATH
中
issue 4
如果已经在 CMakeLists.txt 中使用了CMAKE_AUTOMOC
,则不需要手动调用qt5_wrap_cpp
issue 5
执行cmake --build path/build > build.log
构建 msvc 项目将日志重定向某个文本文档中乱码问题
CMake 增加编译参数和预处理指令
添加编译参数
- 使用
add_compile_options
命令,这个命令将添加到所有的目标上cmake add_compile_options(-Wall)
- 使用
target_compile_options
命令,这个命令只会添加到指定的目标上,这种方法直接修改了 CMake 的全局变量,所以它会影响到所有的目标cmake target_compile_options(target PRIVATE -Wall) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ...")
添加预处理指令
- 使用
add_definitions
命令,这个命令将添加到所有的目标上cmake add_definitions(-DDEBUG)
- 使用
target_compile_definitions
命令,这个命令只会添加到指定的目标上cmake target_compile_definitions(target PRIVATE DEBUG)
PRIVATE
: 只有目标自己会使用这些编译参数PUBLIC
: 目标自己和其他依赖这个目标的目标都会使用这些编译参数INTERFACE
: 只有其他依赖这个目标的目标会使用这些编译参数
set
与 list
的字符串操作
set
拼接
1 |
|
其实就是将从第二个参数开始往后所有的字符串进行拼接,最后将结果存储到第一个参数中,如果第一个参数中有数据就会对元数据进行覆盖
list
获取 list
的长度
1 |
|
LENGTH
:子命令LENGTH
用于读取列表长度<list>
:当前操作的列表<output_variable>
:新创建的变量,用户存储列表长度的结果
读取列表中指定索引的元素
1 |
|
<list>
:当前操作的列表<element_index>
:列表元素的索引- 从开始编号,索引表示 0 的元素为列表中的第一个元素
- 索引也可以是负数,-1 表示列表的最后一个元素,-2 表示倒数第二个元素,以此类推
- 当索引(不管正负)超过列表的长度,运行会报错
<output_variable>
:新创建的变量,用户存储索引元素的返回结果,也是一个列表
将列表中的元素用连接符(字符串)连接起来组成一个字符串
1 |
|
JOIN
:子命令JOIN
用于连接列表数据<list>
:当前操作的列表<glue>
:指定的链接符(字符串)<output_variable>
:新创建的变量,存储返回的字符串
查找 list
列表中是否存在指定的元素, 若未找到返回 -1
1 |
|
FIND
:表示进行查找操作<list>
:表示当前操作的列表<value>
:需要在列表中搜索的元素<output_variable>
:新创建的变量- 如果列表
<list>
中存在<value>
那么返回<value>
在列表中的索引 - 如果未找到则返回 -1
- 如果列表
列表排序
1 |
|
COMPARE
:指定排序方法。有如下几种值可选:STRING
:按照字符顺序进行排序,为默认的排序方法FILE_BASENAME
:如果是一系列路径名,会使用basename
进行排序NATURAL
:使用自然数顺序排序
CASE
:指明大小写是否敏感,有如下两种值可选:SENSITIVE
:按照大小写敏感的方式进行排序,为默认值INSENSITIVE
:按照大小写不敏感方式进行排序
ORDER
:指明排序的顺序,如有几种值可选ASCENDING
:按照升序排列,为默认值DESCENDING
:按照降序排列
其他操作
- 将元素追加到列表尾部中
list(APPEND <list> [<element> ...])
- 将元素插入到列表的 0 索引位置
list(PREPEND <list> [<element> ...])
- 将列表中的最后元素移除
list(POP_BACK <list> [<output_variable> ...])
- 将列表中的第一个元素移除
list(POP_FRONT <list> [<output_variable> ...])
- 将指定元素从列表中移除
list(REMOVE_ITEM <list> [<value> ...])
- 将指定索引的元素从列表中移除
list(REMOVE_AT <list> [<index> ...])
- 移除列表中重复的元素
list(REMOVE_DUPLICATES <list>)
- 列表翻转
list(REVERSE <list>)
虚拟机
VirtualBox Ubuntu22.04 Terminal 无法打开
CTRL + ALT + F3 进入命令行模式(需要返回桌面时 CTRL + ALT + F1)
1 |
|
VirtualBox 如何连接虚拟机与主机
NAT 网络:适用于基本互联网连接
NAT(网络地址转换)是 VirtualBox 默认的网络设置。它允许虚拟机通过主机的网络连接访问互联网,而无需额外的配置。虚拟机在 NAT 模式下无法被主机或局域网中的其他计算机访问,这意味着它具有很高的安全性
- 配置步骤
- 打开 VirtualBox,选择目标虚拟机,点击“设置”
- 进入“网络”选项卡,确保“启用网络适配器”已选中
- 网络连接方式选择“NAT”
- 启动虚拟机,虚拟机将自动获取一个 IP 地址并通过主机的网络连接到互联网
- 优点
- 配置简单,无需进行复杂的网络设置
- 对于只需要互联网连接的虚拟机非常方便
- 缺点
- 虚拟机无法被主机或局域网中的其他设备访问
- 不适用于需要虚拟机与主机或其他虚拟机直接通信的场景
桥接网络:虚拟机与主机及局域网设备互通
桥接网络是 VirtualBox 提供的另一种网络模式,它允许虚拟机像主机一样直接连接到局域网。这意味着虚拟机将获得与主机在同一网段的 IP 地址,并且可以与局域网中的其他设备互相通信。这种模式特别适用于需要虚拟机与主机及其他局域网设备直接通信的场景
- 配置步骤
- 打开 VirtualBox,选择目标虚拟机,点击“设置”
- 进入“网络”选项卡,确保“启用网络适配器”已选中
- 网络连接方式选择“桥接适配器”
- 从下拉菜单中选择主机的网络接口
- 启动虚拟机,虚拟机将自动获取一个与主机同一网段的 IP 地址
- 优点
- 虚拟机与主机及局域网设备互通,无需额外的网络配置
- 适用于需要虚拟机作为网络服务提供者的场景,如 Web 服务器、数据库服务器等
- 缺点
- 需要局域网中有足够的 IP 地址分配给虚拟机
- 配置稍微复杂,需要了解主机的网络接口情况
内部网络:虚拟机之间的独立网络
内部网络是一种特殊的网络模式,它允许多个虚拟机在一个独立的网络中互相通信,但不与主机或局域网设备通信。这种模式特别适用于创建隔离的测试环境
- 配置步骤
- 打开 VirtualBox,选择目标虚拟机,点击“设置”
- 进入“网络”选项卡,确保“启用网络适配器”已选中
- 网络连接方式选择“内部网络”
- 在“名称”字段中输入一个内部网络名称,确保所有需要互通的虚拟机使用相同的网络名称
- 启动虚拟机,根据需求手动配置 IP 地址,确保在同一网段内
- 优点
- 虚拟机之间可以相互通信,适用于创建独立的测试环境
- 不受主机或局域网的网络配置影响
- 缺点
- 虚拟机无法访问互联网或主机网络
- 需要手动配置 IP 地址,可能对网络配置的理解有一定要求
主机专用网络:虚拟机与主机的独立网络
主机专用网络是一种介于 NAT 和内部网络之间的模式,它允许虚拟机与主机在一个独立的网络中通信,但不与其他局域网设备通信。这种模式适用于需要虚拟机与主机直接通信,但不需要外部网络访问的场景
- 配置步骤
- 打开 VirtualBox,选择目标虚拟机,点击“设置”
- 进入“网络”选项卡,确保“启用网络适配器”已选中
- 网络连接方式选择“主机专用适配器”
- 启动虚拟机,虚拟机将自动获取一个主机专用网络的 IP 地址
- 优点
- 虚拟机与主机可以直接通信,适用于开发和测试环境
- 不受局域网配置影响,安全性较高
- 缺点
- 虚拟机无法访问互联网或局域网中的其他设备
- 需要手动配置主机专用网络的设置