doubao_chat/include_lib/c++/README.md

157 lines
7.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# C++ 代码使用说明
C++ 标准库有一些特性会占用较多的空间。一些情况下,我们可能并不会使用,为了兼顾程序的体积,C++ 标准库编译了各种不同的配置。
长跳转是指编译时,添加了 `-mllvm -pi32v2-large-program=true`。这个使得可以`call`更远距离的函数(同时生成的库函数体积也会更大一些)。
## C++ 库列表说明
| 长跳转 | 线程 (thread) | 异常(exception)| locale | 名称 |
|:--------:|:-------------:|:--------------:|:------:|:---:|
| 未支持 | 未支持 | 未支持 | 未支持 | median |
| 未支持 | 未支持 | 未支持 | 支持 | locale-median |
| 未支持 | 支持 | 未支持 | 未支持 | pthread-median |
| 未支持 | 支持 | 未支持 | 支持 | pthread-locale-median |
| 未支持 | 未支持 | 支持 | 未支持 | exception-median |
| 未支持 | 未支持 | 支持 | 支持 | exception-locale-median |
| 未支持 | 支持 | 支持 | 未支持 | exception-pthread-median |
| 未支持 | 支持 | 支持 | 支持 | exception-pthread-locale-median |
| 支持 | 未支持 | 未支持 | 未支持 | large |
| 支持 | 未支持 | 未支持 | 支持 | locale-large |
| 支持 | 支持 | 未支持 | 未支持 | pthread-large |
| 支持 | 支持 | 未支持 | 支持 | pthread-locale-large |
| 支持 | 未支持 | 支持 | 未支持 | exception-large |
| 支持 | 未支持 | 支持 | 支持 | exception-locale-large |
| 支持 | 支持 | 支持 | 未支持 | exception-pthread-large |
| 支持 | 支持 | 支持 | 支持 | exception-pthread-locale-large |
## 集成的步骤
为了集成 C++ 标准库,主要需要做下面几个步骤:
1. 确定需要使用的 C++ 特性,选择对应的 C++ 标准库
2. 编译参数添加标准库的搜索路径,例如 `-I ${CXXDIR}/large/include/c++/v1`。注意需要将其添加到 C 标准库的搜索路径之前,即 `-I /opt/jieli/pi32v2/include` 之前。
3. 链接参数中,需要添加 `libc++.a`, `libc++abi.a` 的路径。
- 如果使用了异常,还需要增加 `libunwind.a`。且链接参数增加 `--eh-frame-hdr`
- 如果使用了 TLS 变量,还需要增加 `libemutls.a`(且需要依赖 `pthread`)
4. 链接参数建议加上 `--gc-sections` 以缩减体积。
5. 处理全局构造函数,参考下面注意事项。
6. 一些函数可能在 C 库中确实(未提供,例如 `_malloc_r`, `sscanf` 等)。需要在 SDK 中补充实现。
## 注意事项
1. 我们使用的是 clang 4.0.1,可以支持到 c++14 以及部分的 c++17
分别是:
- `-std=c++11`, `-std=gnu++11` # c++11
- `-std=c++14`, `-std=gnu++14` # c++14
- `-std=c++1z`, `-std=gnu++1z` # c++17 (部分)
建议都使用 gnu++1n 的写法。这样不会定义 `__STRICT_ANSI__` 宏,避免问题(如果定义了`__STRICT_ANSI__`,一些函数会缺少声明)。
2. clang 编译 `.c` 后缀的时候,是 C 编译器。
编译 `.cxx`, `.cc`, `.cpp` 文件的时候,是当做 C++ 模式处理。
所以注意当前文件后缀是否正确。
3. C++ 标准库使用的版本是 llvm-12.0.1:
- 不支持的部分:
`filesystem`
`std::cin`, `std::cout`
`fstream` 相关操作
- 支持的部分:
STL(`vector`, `list`, `map`, `functional`, `algorithm` 等)
`thread`, `mutex`, `future` 等 (依赖于 `pthread` 接口实现)
`strstream` 等(依赖于 `locale`)
4. 如果要使用 TLS 变量,即 `thread_local` 以及 `__thread` 等变量。
则需要编译参数加上 `-femulated-tls`,链接参数加上 `--plugin-opt=-emulated-tls`。
链接库需要加上 `libemutls.a`,且依赖 `pthread` 接口。
5. 可以支持异常,异常使用的是基于 dwarf (`.eh_frame`)的处理方式,也就是编译器静态生成栈帧回退的数据结构,在抛出异常时候才会使用。
链接脚本需要增加下面的部分:
```ld
.text : {
*(.text*)
*(.gcc_except_table*);
}
.eh_frame : {
__eh_frame_start = .;
KEEP(*(.eh_frame)); // 注意要有 KEEP
__eh_frame_end = .;
}
.eh_frame_hdr : {
__eh_frame_hdr_start = .;
KEEP(*(.eh_frame_hdr)); // 注意要有 KEEP
__eh_frame_hdr_end = .;
}
```
在运行时,需要把 `.eh_frame`, `.eh_frame_hdr` 放到 flash 中(也可内存,这两部分数据是只读的)
链接参数还需要加上 `--eh-frame-hdr` 这样才会生成 `.eh_frame_hdr` 段。用以异常处理。
编译 C 代码时,需要加上 `-funwind-tables`,避免在 C 代码部分异常未能正确回滚栈帧。
需要额外链接 libunwind.a。
会增加较多的额外代码以及数据量,固件体积会增加较多。一般情况下,不推荐使用异常。
6. 一般情况下,推荐关闭异常,以及 RTTI:`-fno-exceptions`, `-fno-rtti`
如果使用了 `dynamic_cast`,则需要保留 RTTI,不能使用 `-fno-rtti`
7. 头文件引用顺序
1. 注意,先搜索 C++ 头文件,再搜索 C 的头文件。避免冲突。也就是,先 -I C++ 的标准库头文件路径,再 -I C 的标准库头文件路径
2. 如果用了 `pthread`,则注意不能使用到 C 标准库里面的 `pthread.h`。建议直接弃用系统路径的 C 标准库(拷贝一份到 SDK,并删除其中的 `pthread.h`)
3. `pthread` 需要使用 `simple_pthread` 文件夹中提供的部分。这个里面的 `pthread.h` 和 `semaphore.h` 是手动将 OS 相关的内容提取出来,生成的头文件,保证了较少的外部依赖(`clang -E -p` 参数进行宏展开后,再手动拷贝相关函数声明以及结构体定义)
8. 当需要使用 `iostream`, `strstream` 等相关的内容时候,需要依赖于 `locale` 相关的 api
如果有报错 `error "Localization is not supported by this configuration of libc++"` 则说明需要换一个 libc++ 的库
9. 处理全局变量的构造和析构。
链接脚本添加:
```ld
// 全局变量的构造函数列表
___ctors_begin = .;
KEEP(*(SORT(.ctors.*))) // 注意要有 KEEP
KEEP(*(.ctors)) // 注意要有 KEEP
___ctors_end = .;
```
在 main 函数调用前,调用全局变量的构造函数:
```c
typedef void (*pfunc) ();
extern pfunc ___ctors_begin[];
extern pfunc ___ctors_end[];
pfunc *p;
// 调用全局构造函数(注意是要逆序)
for (p = ___ctors_end; p > ___ctors_begin; )
(*--p) (); // 注意这里是倒序调用的
```
需要添加 `__cxa_atexit`, `__cxa_finalize` 函数:
```c
// https://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/baselib---cxa-atexit.html
int __cxa_atexit(void (*func) (void *), void * arg, void * dso_handle);
void __cxa_finalize(void *f);
```
需要添加 `__dso_handle` 定义:
```c
// 这个在调用 __cxa_atexit 的时候需要用到
void *__dso_handle = &__dso_handle;
```
10. `new`, `delete`, `__cxa_pure_virtual`, `__cxa_deleted_virtual` 这些函数在 `libc++abi.a` 中已经定义