命名空间
变体
操作

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

来自 cppreference.com

#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) 匹配。
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(参见:属性),但由预处理标记而不是标记组成。
pp-balanced-token-sequence - 一个 balanced-token-sequence(参见:属性),但由预处理标记而不是标记组成

[编辑] 解释

1) 以实现定义的方式搜索由 h-char-sequence 标识的资源。
2) 以实现定义的方式搜索由 q-char-sequence 标识的资源。对于 (1,2),实现通常使用与 源文件包含 所使用的实现定义搜索路径类似但不同的机制。构造 __has_embed(__FILE__ ... 出现在标准中的一个示例中,表明,至少在 (2) 的情况下,预计会搜索当前文件所在的目录。
3) 指令中 embed 后的预处理标记的处理方式与普通文本相同(即,当前定义为宏名称的每个标识符都将替换为其预处理标记的替换列表)。所有替换后产生的指令应匹配之前两种形式中的一种。将 <> 预处理标记对或一对 " 字符之间的预处理标记序列组合成单个头文件名预处理标记的方法是实现定义的。
4)pp-tokens 在语法 (3) 中的方式搜索由 h-char-sequenceq-char-sequence 标识的资源,但不会进行进一步的宏扩展。如果这样的指令不能满足 #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 指令的扩展是由下面描述的整数 常量表达式 列表形成的标记序列。列表中每个整数常量表达式的标记组在标记序列中与列表中前一个整数常量表达式的标记组之间用逗号隔开。该序列既不以逗号开头也不以逗号结尾。如果整数常量表达式的列表为空,则标记序列为空。该指令将被其扩展替换,并且,在存在某些嵌入参数的情况下,会添加或替换标记序列。

扩展序列中整数常量表达式的值由对资源数据的实现定义映射确定。每个整数常量表达式的值都在范围内 [02embed element width)。如果

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

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

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

[编辑] 参数

标准定义了参数 limitprefixsuffixif_empty。指令中出现的任何其他参数必须是实现定义的,否则程序格式不正确。实现定义的嵌入参数可能会改变指令的语义。

[编辑] limit

limit( 常量表达式 ) (1)
__limit__( 常量表达式 ) (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)