Binary resource inclusion (since C23)
#embed 是一个用于在构建过程中包含(二进制)资源的预处理器指令,其中资源被定义为可从翻译环境访问的数据源。
目录 |
语法
#embed <
h-字符序列
>
嵌入参数序列
(可选)
换行符
|
(1) | ||||||||
#embed "
q-字符序列
"
嵌入参数序列
(可选)
换行符
|
(2) | ||||||||
#embed
预处理记号
换行符
|
(3) | ||||||||
__has_embed
(
"
q-字符序列
"
嵌入参数序列
(可选)
)
__has_embed
(
<
h-字符序列
>
嵌入参数序列
(可选)
)
|
(4) | ||||||||
__has_embed
(
字符串字面量
预处理平衡记号序列
(可选)
)
__has_embed
(
<
h-预处理记号
>
预处理平衡记号序列
(可选)
)
|
(5) | ||||||||
| new-line | - | 换行符 |
| 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 可在 #if 和 #elif 的表达式中展开。它会被 #ifdef 、 #ifndef 、 #elifdef 、 #elifndef 以及 defined 视为已定义的宏,但不能在其他任何地方使用。
资源具有
实现资源宽度
,即实现定义的定位资源的位大小。其
资源宽度
默认为实现资源宽度,除非被
limit
参数修改。若资源宽度为0,则该资源被视为空资源。
嵌入元素宽度
等于
CHAR_BIT
,除非被实现定义参数修改。资源宽度必须可被嵌入元素宽度整除。
#embed
指令的展开是一个由下述整数
常量表达式
列表构成的记号序列。列表中每个整数常量表达式对应的记号组,会通过逗号与前一整数常量表达式的记号组分隔。该序列既不以逗号开头也不以逗号结尾。若整数常量表达式列表为空,则记号序列为空。该指令会被其展开内容替换,并且在存在特定嵌入参数时,还会包含额外或替代的记号序列。
扩展序列中整型常量表达式的值由资源数据的实现定义映射所确定。每个整型常量表达式的值在区间
[
0
,
2
embed element width
)
内。若满足:
- 整数常量表达式列表用于初始化与 unsigned char 兼容类型的数组,或者当 char 无法容纳负值时与 char 兼容类型的数组,且
- 嵌入元素的宽度等于 CHAR_BIT ,
那么数组已初始化元素的内容就如同资源的二进制数据在翻译时被 fread 读入数组一般。
鼓励实现同时考虑翻译时的位序和字节序以及执行时的位序和字节序,以更准确地表示指令中资源的二进制数据。这能最大化保证:若通过 #embed 指令在翻译时引用的资源与通过执行时方式访问的资源相同,则通过例如 fread 或其他方式读入连续存储的数据,将与由 #embed 指令扩展内容初始化的字符类型数组进行逐位比对时完全一致。
参数
标准定义了参数
limit
、
prefix
、
suffix
和
if_empty
。指令中出现的任何其他参数必须是实现定义的,否则程序格式错误。实现定义的嵌入参数可能会改变指令的语义。
限制
limit(
常量表达式
)
|
(1) | ||||||||
__limit__(
常量表达式
)
|
(2) | ||||||||
limit
嵌入参数在嵌入参数序列中最多只能出现一次。它必须带有一个参数,该参数必须是求值为非负数的整型(预处理器)
常量表达式
,且不能包含
defined
标记。资源宽度被设置为该整型常量表达式乘以嵌入元素宽度与实现资源宽度两者中的较小值。
suffix
suffix(
预处理平衡记号序列
(可选)
)
|
(1) | ||||||||
__suffix__(
预处理平衡记号序列
(可选)
)
|
(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' // 空终止符 }; 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++ documentation
for
资源包含
(自 C++26 起)
|