命名空间
变体
操作

二进制资源包含 (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) 匹配。
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) 搜索由 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 起)