二进制资源包含 (自 C23 起)
#embed 是一个预处理器指令,用于在构建中包含(二进制)资源,其中资源被定义为可从翻译环境访问的数据源。
内容 |
[编辑] 语法
#embed < h-char-sequence > embed-parameter-sequence (可选) new-line |
(1) | ||||||||
#embed " q-char-sequence " embed-parameter-sequence (可选) new-line |
(2) | ||||||||
#embed pp-tokens new-line |
(3) | ||||||||
__has_embed ( " q-char-sequence " embed-parameter-sequence (可选) ) __has_embed ( < h-char-sequence > embed-parameter-sequence (可选) ) |
(4) | ||||||||
__has_embed ( string-literal pp-balanced-token-sequence (可选) ) __has_embed ( < h-pp-tokens > pp-balanced-token-sequence (可选) ) |
(5) | ||||||||
换行符 | - | 换行符 |
h-char-sequence | - | 一个或多个 h-char 的序列,其中出现以下任何字符都会导致未定义行为
|
h-char | - | 源字符集中的任何成员,除了换行符和 > |
q-char-sequence | - | 一个或多个 q-char 的序列,其中出现以下任何字符都会导致未定义行为
|
q-char | - | 源字符集中的任何成员,除了换行符和 " |
pp-tokens | - | 一个或多个预处理标记的序列 |
string-literal | - | 一个字符串字面量 |
h-pp-tokens | - | 一个或多个预处理标记的序列,除了 > |
embed-parameter-sequence | - | 一个或多个 pp-parameter 的序列。请注意,与 attribute-list 不同,此序列不是逗号分隔的。 |
pp-parameter | - | 一个 attribute-token (参见:attributes),但由预处理标记而不是标记组成。 |
pp-balanced-token-sequence | - | 一个 balanced-token-sequence (参见:attributes),但由预处理标记而不是标记组成 |
[编辑] 解释
embed
之后的预处理标记的处理方式与普通文本相同(即,每个当前定义为宏名称的标识符都将替换为其替换预处理标记列表)。所有替换后产生的指令应与前两种形式之一匹配。将 < 和 > 预处理标记对或一对 " 字符之间的预处理标记序列组合成单个头文件名称预处理标记的方法是实现定义的。__has__embed
表达式求值为 __STDC_EMBED_FOUND__
;如果资源为空且所有参数都受支持,则求值为 __STDC_EMBED_EMPTY__
;如果搜索失败或传递的参数之一不受实现支持,则求值为 __STDC_EMBED_NOT_FOUND__
。如果找不到资源或实现不支持其中一个参数,则程序是非良构的。
__has_embed
可以在 #if 和 #elif 的表达式中展开。它被 #ifdef、 #ifndef、 #elifdef、 #elifndef 和 defined 视为已定义的宏,但不能在其他任何地方使用。
资源具有实现资源宽度,它是已定位资源的实现定义的位大小。其资源宽度是实现资源宽度,除非被 limit
参数修改。如果资源宽度为 0,则资源被视为空。嵌入元素宽度等于 CHAR_BIT,除非被实现定义的参数修改。资源宽度必须可被嵌入元素宽度整除。
#embed 指令的展开是由下面描述的整数常量表达式列表形成的标记序列。列表中每个整数常量表达式的标记组在标记序列中与列表中前一个整数常量表达式的标记组之间用逗号分隔。该序列既不以逗号开头也不以逗号结尾。如果整数常量表达式列表为空,则标记序列为空。该指令被其展开替换,并且在存在某些嵌入参数的情况下,还会有附加的或替换的标记序列。
展开序列中整数常量表达式的值由资源数据的实现定义的映射确定。每个整数常量表达式的值都在范围 [
0,
2嵌入元素宽度)
内。
- 整数常量表达式列表用于初始化与
unsigned char
兼容的类型数组,或者如果char
不能容纳负值,则与char
兼容的类型数组,并且 - 嵌入元素宽度等于 CHAR_BIT,
那么,数组的初始化元素的内容就如同资源的二进制数据在翻译时被 fread 到数组中一样。
鼓励实现考虑翻译时和执行时的位和字节顺序,以便更适当地表示指令中资源的二进制数据。这最大限度地提高了以下可能性:如果在翻译时通过 #embed 指令引用的资源与通过执行时方式访问的资源相同,则例如 fread 或类似方式读取到连续存储中的数据将与从 #embed 指令的展开内容初始化的字符类型数组逐位比较相等。
[编辑] 参数
标准定义了参数 limit
、prefix
、suffix
和 if_empty
。指令中出现的任何其他参数都必须是实现定义的,否则程序是非良构的。实现定义的嵌入参数可能会更改指令的语义。
[编辑] limit
limit( constant-expression ) |
(1) | ||||||||
__limit__( constant-expression ) |
(2) | ||||||||
limit
嵌入参数在嵌入参数序列中最多只能出现一次。它必须有一个参数,该参数必须是一个整数(预处理器)常量表达式,其求值结果为非负数,并且不包含标记 defined
。资源宽度设置为整数常量表达式乘以嵌入元素宽度与实现资源宽度的最小值。
[编辑] suffix
suffix( pp-balanced-token-sequence (可选) ) |
(1) | ||||||||
__suffix__( pp-balanced-token-sequence (可选) ) |
(2) | ||||||||
suffix
嵌入参数在嵌入参数序列中最多只能出现一次。它必须有一个(可能为空)预处理器参数子句。如果资源非空,则参数子句的内容将紧跟在指令展开之后放置。否则,它不起作用。
[编辑] prefix
prefix( pp-balanced-token-sequence (可选) ) |
(1) | ||||||||
__prefix__( pp-balanced-token-sequence (可选) ) |
(2) | ||||||||
prefix
嵌入参数在嵌入参数序列中最多只能出现一次。它必须有一个(可能为空)预处理器参数子句。如果资源非空,则参数子句的内容将紧靠在指令展开之前放置。否则,它不起作用。
[编辑] if_empty
if_empty( pp-balanced-token-sequence (可选) ) |
(1) | ||||||||
__if_empty__( pp-balanced-token-sequence (可选) ) |
(2) | ||||||||
if_empty
嵌入参数在嵌入参数序列中最多只能出现一次。它必须有一个(可能为空)预处理器参数子句。如果资源为空,则参数子句的内容将替换该指令。否则,它不起作用。
[编辑] 示例
#include <stdint.h> #include <stdio.h> const uint8_t image_data[] = { #embed "image.png" }; const char message[] = { #embed "message.txt" if_empty('M', 'i', 's', 's', 'i', 'n', 'g', '\n') ,'\0' // null terminator }; void dump(const uint8_t arr[], size_t size) { for (size_t i = 0; i != size; ++i) printf("%02X%c", arr[i], (i + 1) % 16 ? ' ' : '\n'); puts(""); } int main() { puts("image_data[]:"); dump(image_data, sizeof image_data); puts("message[]:"); dump((const uint8_t*)message, sizeof message); }
可能的输出
image_data[]: 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 00 00 00 01 00 00 00 01 01 03 00 00 00 25 DB 56 ... message[]: 4D 69 73 73 69 6E 67 0A 00
[编辑] 参考
- C23 标准 (ISO/IEC 9899:2024)
- 6.4.7 头文件名称 (p: 69)
- 6.10.1 条件包含 (p: 165-169)
- 6.10.2 二进制资源包含 (p: 170-177)
[编辑] 参见
C++ 文档 关于 资源包含 (自 C++26 起)
|