自动驾驶工具工作记录

C++

sregex_token_iterator的特性

string 转数字

关于时间的函数和结构体

time 函数

1
2
#include <time.h>
time_t time(time_t* calptr)

得到自 1970-1-1 00:00:00 以来经过的秒数,结果可以通过返回值,也可以通过参数得到

1
2
3
4
time_t now;
time(&now);
// 等同于 now = time(NULL)
printf("now time is %d\n", now);

localtime

1
2
3
4
5
6
7
8
9
10
11
12
13
struct tm {
int tm_sec; /* 秒 – 取值区间为 [0,59] */
int tm_min; /* 分 - 取值区间为 [0,59] */
int tm_hour; /* 时 - 取值区间为 [0,23] */
int tm_mday; /* 一个月中的日期 - 取值区间为 [1,31] */
int tm_mon; /* 月份(从一月开始,0 代表一月) - 取值区间为 [0,11] */
int tm_year; /* 年份,其值等于实际年份减去 1900 */
int tm_wday; /* 星期 – 取值区间为 [0,6],其中 0 代表星期天,1 代表星期一 */
int tm_yday; /* 从每年 1 月 1 日开始的天数– 取值区间 [0,365],其中 0 代表 1 月 1 日 */
int tm_isdst; /* 夏令时标识符,夏令时 tm_isdst 为正;不实行夏令时 tm_isdst 为 0 */
};

struct tm *localtime(const time_t* calptr);

用来获取系统时间,精度为秒;将时间(秒)数值变换成本地时间,考虑到本地时区和夏令时标志

1
2
3
4
5
6
7
8
9
10
11
12
13
time_t now;
struct tm *tm_now;
time(&now);
tm_now = localtime(&now);
// 把秒变换成年月日
printf("now datetime: %d-%d-%d %d:%d:%d\n",
tm_now->tm_year+1900,
tm_now->tm_mon+1,
tm_now->tm_mday,
tm_now->tm_hour,
tm_now->tm_min,
tm_now->tm_sec
);

localtime_r

1
struct tm *localtime_r(const time_t *timep, struct tm *result);

用来获取系统时间,运行于 linux 平台下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <time.h>

int main()
{
time_t time_seconds = time(0);
struct tm now_time;
localtime_r(&time_seconds, &now_time);

printf("%d-%d-%d %d:%d:%d\n",
now_time.tm_year + 1900,
now_time.tm_mon + 1,
now_time.tm_mday,
now_time.tm_hour,
now_time.tm_min,
now_time.tm_sec
);
}

localtime_s

用来获取系统时间,运行于 windows 平台下,与 localtime_r 只有参数顺序不一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 #include <iostream>
#include <time.h>

int main()
{
time_t time_seconds = time(0);
struct tm now_time;
localtime_s(&now_time,&time_seconds);
printf("%d-%d-%d %d:%d:%d\n",
now_time.tm_year + 1900,
now_time.tm_mon + 1,
now_time.tm_mday,
now_time.tm_hour,
now_time.tm_min,
now_time.tm_sec
);
}

localtimelocaltime_r 二者区别

  • localtime 对于多线程不安全,因为 localtime 在使用时,只需定义一个指针,申请空间的动作由函数自己完成,这样在多线程的情况下,如果有另一个线程调用了这个函数,那么指针指向的 struct tm结构体的数据就会改变
  • 在 localtime_slocaltime_r 调用时,定义的是 struct tm 的结构体,获取到的时间已经保存在 struct tm 中,并不会受其他线程的影响

gettimeofday

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <sys/time.h>

struct timezone {
int tz_minuteswest;/* 格林威治时间往西方的时差 */
int tz_dsttime;/*DST 时间的修正方式 */
}

struct timeval {
long int tv_sec; // 秒数
long int tv_usec; // 微秒数
}

int gettimeofday(struct timeeval* tv, struct timezone* tz);

获取秒、微秒、时区等信息;其参数 tv 是保存获取时间结果的结构体,参数 tz 用于保存时区结果,timezone参数若不使用则传入 NULL 即可

1
2
3
4
struct timeval tv_begin, tv_end;
gettimeofday(&tv_begin, NULL);
foo();
gettimeofday(&tv_end, NULL);

它获得的时间精确到微秒 1e-6s 量级,在一段代码前后分别使用 gettimeofday 可以计算代码执行时间

linux 下 sprintf_s 函数的替代

windows 平台下线程安全的格式化字符串函数sprint_s 并非标准 C 函数,因此 linux 下无法使用,但可以使用 snprintf 函数代替

1
2
3
4
5
6
7
8
9
10
11
12
/* 函数原型:*/
int snprintf(char *dest, size_t n, const char *fmt, ...);

/* 函数说明:最多从源串中拷贝 n-1 个字符到目标串中,然后再在后面加一个 0。所以如果目标串的大小为 n 的话,将不会溢出。
函数返回值:若成功则返回存入数组的字符数,若编码出错则返回负值。
推荐的用法:*/
void f(const char *p)
{
char buf[11] = { 0 };
snprintf(buf, sizeof(buf), "%10s", p); // 注意:这里第 2 个参数应当用 sizeof(str),而不要使用硬编码 11,也不应当使用 sizeof(str)-1 或 10
printf("%sn", buf);
}

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
  • 不同的编译环境可以使用宏定义区分
    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::filesystemnamespace 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
2
3
...
SysPrint::instance()->print(u8" 找到硬件解码器 hevc_qsv");
...

Visual Studio 无法打开编译器生成的文件:“xxx.obj”: Permission denied

删除其他配置平台编译后的文件,如 Debug 或 Relase 文件目录

不允许 dllimport 静态数据成员的定义

当要使用一个类的时候必须要有其定义,有两种方式:

  • 引用其头文件,即include "xxx.h"

  • 使用导出类,即使用 __declspec(dllimport) 定义的类为导出类

    1
    2
    3
    4
    class __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
2
3
4
5
6
7
8
9
10
#if defined(LIBRARY)
#define SHARED_EXPORT Q_

DECL_EXPORT
#else
#define SHARED_EXPORT Q_DECL_IMPORT
#endif

#define Q_DECL_EXPORT __declspec(dllexport)
#define Q_DECL_IMPORT __declspec(dllimport)

Windows 编程

WMIC 指令

windows 批处理 打开 exe 后关闭黑窗口

1
start "" "xxx.exe"

如果下面这样调用,需要等待程序窗口关闭之后窗黑口才会关闭的
xxx.exe exit

Linux

从 windows 拷贝文件到 WSL 最快速的方法

  • 在 WSL 用 sudo ls /mnt/* 列出系统所有的挂载盘,可以看到 windows 系统的所有盘都列出来了
  • 然后使用 cp 命令

Git

递归 git push/pull

在 Git 中,递归 git push/pull 操作是指在父模块中递归地操作子模块的 push 和 pull 操作。当一个代码库包含多个子模块时,可以使用递归 git push/pull 操作来同时对所有子模块进行相关操作,从而避免了手动逐个操作子模块的繁琐

递归git push

1
git push --recurse-submodules=on-demand

递归git pull

1
git pull --recurse-submodules
  • 确保所有子模块都已经初始化和更新。如果有尚未初始化或更新的子模块,可以使用 git submodule initgit submodule update命令来初始化和更新子模块
  • 如果在父模块中进行了修改,并且同时也在某个子模块中进行了修改,那么在递归 git push/pull 操作时可能会出现冲突。如果出现冲突,需要手动解决冲突并再次进行递归 git push/pull 操作
  • 在进行递归 git push/pull 操作时,确保所有子模块的远程仓库地址正确设置,并且拥有 push/pull 权限

如何删除 Git 中的未跟踪文件 untracked files

要删除所有未跟踪的文件,可以运行以下命令

1
git clean -f # 该命令中的 -f 参数表示强制执行删除操作

如果想要删除未跟踪的文件和目录,可以使用 -d 参数

1
git clean -f -d

解决 Git 报 error unknown switch `e‘ 错误

在 VS Code 中,使用 git stash pop [<stash>] 语法报错

1
2
git stash pop stash@{0}
error unknown switch `e‘

花括号在 PowerShell 中被认为是代码块执行标识符,若想正常使用,可用反引号 ` 进行转义

1
git stash pop stash@`{0`}

git stash 某个特定的文件

可以使用 git stash 命令结合 git checkout 和 git add 来实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 假设想要暂存文件 'specific_file.txt'

# 检查当前状态
git status

# 重置或暂存其他文件
# 例如,如果想暂存 'unwanted_file.txt',可以丢弃更改
git checkout -- unwanted_file.txt

# 或者如果想保留这些更改,可以将它们添加到暂存区
git add unwanted_file.txt

# 暂存特定文件
git stash push -m "stash specific_file.txt" specific_file.txt

# 现在 specific_file.txt 被暂存了,而其他更改仍然在工作目录中
# 也可以使用 git checkout-index 命令结合 git stash:

# 将特定文件复制到暂存区
git checkout-index --stdin < <(git diff --name-only specific_file.txt | git ls-files --stdin)

# 暂存所有更改,包括特定文件
git stash save -k "stash specific_file.txt"

# 现在,从暂存区中移除特定文件,这样它就不会出现在下一次提交中
git reset specific_file.txt

请注意,-k选项告诉 git stash 不要暂存那些已经被添加到暂存区的文件

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
ADD_LIBRARY(${PROJECT} ${SRC_FILES} ${HEADER_FILES}) 

由于 CMakeLists.txt 文件中没有定义 ${PROJECT} 变量,ADD_LIBRARY少了一个参数导致报错

1
ADD_LIBRARY($XXX ${SRC_FILES} ${HEADER_FILES})

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
TARGET_LINK_LIBRARIES(${PROJECT} Qt5::Widgets Qt5::Core)

issue 3

needed by xxx.so, not found (try using -rpath or -rpath-link)

没有将自己编译的动态库导入到 LD_LIBRARY_PATH


自动驾驶工具工作记录
https://silhouettesforyou.github.io/2024/08/20/dde743d413c4/
Author
Xiaoming
Posted on
August 20, 2024
Licensed under