命名空间
变体
操作

二进制资源包含 (自 C23 起)

来自 cppreference.cn

#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)
1) 搜索由 h-char-sequence 唯一标识的资源,并将该指令替换为与资源数据对应的逗号分隔的整数列表。
2) 搜索由 q-char-sequence 标识的资源,并将该指令替换为与资源数据对应的整数列表。它可能会回退到 (1)
3) 如果 (1)(2) 都不匹配,则 pp-tokens 将进行宏替换。替换后的指令将再次尝试与 (1)(2) 匹配。
4) 检查资源是否可用于嵌入,它是否为空,以及传递的参数是否受实现支持。
5) 如果 (4) 不匹配,则 h-pp-tokenspp-balanced-token-sequence 将进行宏替换。替换后的指令将再次尝试与 (4) 匹配。
换行符 - 换行符
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),但由预处理标记而不是标记组成

[编辑] 解释

1) 以实现定义的方式搜索由 h-char-sequence 标识的资源。
2) 以实现定义的方式搜索由 q-char-sequence 标识的资源。对于 (1,2),实现通常使用类似于但不等同于用于源文件包含的实现定义搜索路径的机制。构造 __has_embed(__FILE__ ... 出现在标准中的一个示例中,这表明,至少在 (2) 的情况下,期望搜索当前文件所在的目录。
3) 指令中 embed 之后的预处理标记的处理方式与普通文本相同(即,每个当前定义为宏名称的标识符都将替换为其替换预处理标记列表)。所有替换后产生的指令应与前两种形式之一匹配。将 <> 预处理标记对或一对 " 字符之间的预处理标记序列组合成单个头文件名称预处理标记的方法是实现定义的。
4) 搜索由 h-char-sequenceq-char-sequence 标识的资源,就好像该预处理标记序列是语法 (3) 中的 pp-tokens 一样,但不会执行进一步的宏展开。如果这样的指令不满足 #embed 指令的语法要求,则程序是非良构的。如果资源搜索成功,资源非空且所有参数都受支持,则 __has__embed 表达式求值为 __STDC_EMBED_FOUND__;如果资源为空且所有参数都受支持,则求值为 __STDC_EMBED_EMPTY__;如果搜索失败或传递的参数之一不受实现支持,则求值为 __STDC_EMBED_NOT_FOUND__
5) 仅当语法 (4) 不匹配时才考虑此形式,在这种情况下,预处理标记的处理方式与普通文本相同。

如果找不到资源或实现不支持其中一个参数,则程序是非良构的。

__has_embed 可以在 #if #elif 的表达式中展开。它被 #ifdef #ifndef #elifdef #elifndefdefined 视为已定义的宏,但不能在其他任何地方使用。

资源具有实现资源宽度,它是已定位资源的实现定义的位大小。其资源宽度是实现资源宽度,除非被 limit 参数修改。如果资源宽度为 0,则资源被视为空。嵌入元素宽度等于 CHAR_BIT,除非被实现定义的参数修改。资源宽度必须可被嵌入元素宽度整除。

#embed 指令的展开是由下面描述的整数常量表达式列表形成的标记序列。列表中每个整数常量表达式的标记组在标记序列中与列表中前一个整数常量表达式的标记组之间用逗号分隔。该序列既不以逗号开头也不以逗号结尾。如果整数常量表达式列表为空,则标记序列为空。该指令被其展开替换,并且在存在某些嵌入参数的情况下,还会有附加的或替换的标记序列。

展开序列中整数常量表达式的值由资源数据的实现定义的映射确定。每个整数常量表达式的值都在范围 [02嵌入元素宽度) 内。

  1. 整数常量表达式列表用于初始化与 unsigned char 兼容的类型数组,或者如果 char 不能容纳负值,则与 char 兼容的类型数组,并且
  2. 嵌入元素宽度等于 CHAR_BIT

那么,数组的初始化元素的内容就如同资源的二进制数据在翻译时被 fread 到数组中一样。

鼓励实现考虑翻译时和执行时的位和字节顺序,以便更适当地表示指令中资源的二进制数据。这最大限度地提高了以下可能性:如果在翻译时通过 #embed 指令引用的资源与通过执行时方式访问的资源相同,则例如 fread 或类似方式读取到连续存储中的数据将与从 #embed 指令的展开内容初始化的字符类型数组逐位比较相等。

[编辑] 参数

标准定义了参数 limitprefixsuffixif_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 起)