资源包含 (C++26 起)

来自cppreference.com


 
 
C++ 语言
 
 

#embed 是用于包含资源的预处理器指令。

目录

[编辑] 语法

#embed < h字符序列 > 记号序列 换行 (1)
#embed " q字符序列 " 记号序列 换行 (2)
#embed 记号序列 换行 (3)
__has_embed ( 平衡记号序列 ) (4)
1) 搜索由h字符序列 唯一识别的资源,并将该指令替换为这个资源的全部内容。
2) 搜索由q字符序列 识别的资源,并将该指令替换为这个资源的全部内容。可能退回至语法 (1) 并将q字符序列 视为资源标识符。
3) 如果 (1)(2) 都不匹配,记号序列 会经历宏替换。该指令在替换后会再次尝试匹配 (1)(2)
4) 检查是否可以以指定嵌入参数包含一个资源。
换行 - 换行字符
h字符序列 - 一个或多个h字符 的序列(见 #include
q字符序列 - 一个或多个q字符 的序列(见 #include
记号序列 - 一个或多个预处理记号的序列
平衡记号序列 - 一个或多个预处理记号的序列,其中每个 ([{ 都有正确关闭

[编辑] 解释

1) 在一系列地点中搜索由h字符序列 唯一识别的资源,并将该指令替换为这个资源的全部内容。由实现定义如何指定这些地点和识别标头。
2) 将该指令替换为由q字符序列 识别的资源的全部内容。所指名的资源通过由实现定义的方式进行搜索。
如果不支持这种搜索或者搜索失败,该指令按语法 (1) 重新处理,将原指令中包含的序列(包括 > 字符,如果存在)作为语法 (1) 中所需的序列。
3) 该指令中 embed 后面的预处理记号会按在正常文本中进行处理(即每个目前定义为宏名的标识符都会被替换为它的预处理记号替换列表)。
如果在所有替换都完成后生成的指令不匹配前面两种语法,那么行为未定义。
将在一对预处理记号 <> 之间或一对 " 字符之前的预处理记号序列合并为单个资源预处理记号的方法由实现定义。
4)平衡记号序列 用作具有语法 (3) 的某个虚设 #embed 指令的记号序列 并搜索资源。
  • 如果刚才的语法 (3) 指令不满足 #embed 指令的语法要求,那么程序非良构。
  • 否则,如果资源搜索成功且所有给定的嵌入参数均受支持,那么 __has_embed 表达式在资源非空时求值为 __STDC_EMBED_FOUND__,在资源为空时求值为 __STDC_EMBED_EMPTY__
  • 否则 __has_embed 表达式求值为 __STDC_EMBED_NOT_FOUND__

[编辑] 资源

资源 是可以从翻译环境访问的数据源。资源具有实现资源宽度,它是由实现定义的以位表示的资源大小。如果实现资源宽度不是 CHAR_BIT 的整数倍数,那么程序非良构。

实现资源计数 为实现资源宽度除以 CHAR_BIT 的结果。每个资源也具有资源计数,它是实现资源计数本身,除非提供了 limit 嵌入参数。

如果资源计数为零,那么该资源为

// 实现资源宽度是 6 位时程序非良构
#embed "6_bits.bin"

[编辑] 嵌入资源

除非另有修改,#embed 指令会被替换为逗号分隔的 int 类型整数字面量

该逗号分隔列表中的整数字面量对应从作为二进制文件的资源连续调用资源计数次 std::fgetc。如果其中一次 std::fgetc 调用返回 EOF,那么程序非良构。

int i =
{
#embed "i.dat"
}; // i.dat 产生单个值的情况下良构
 
int i2 =
#embed "i.dat"
; // i.dat 产生单个值的情况下也良构
 
struct T
{
    double a, b, c;
    struct { double e, f, g; } x;
    double h, i, j;
};
T x =
{
// 指令产生九个值或更少的情况下良构
#embed "s.dat"
};

[编辑] 嵌入参数

如果语法 (1) 或语法 (2) 中有出现记号序列,那么它会按在正常文本中进行处理。处理后的记号序列 应组成一个嵌入参数 序列,否则程序非良构。嵌入参数具有以下语法:

limit ( 平衡记号序列 ) (1)
prefix ( 平衡记号序列 (可选) ) (2)
suffix ( 平衡记号序列 (可选) ) (3)
if_empty ( 平衡记号序列 (可选) ) (4)
标识符 :: 标识符 (5)
标识符 :: 标识符 ( 平衡记号序列 (可选) ) (6)
1-4) 标准嵌入参数。
1) 限制嵌入的资源的资源计数。
2) 向嵌入的非空资源添加前缀。
3) 向嵌入的非空资源添加后缀。
4) 在嵌入的资源为空时将其替换。
5,6) 非标准嵌入参数。这种参数受条件性支持,并具有由实现定义的语义。

[编辑] limit 参数

形式为 limit ( 平衡记号序列 ) 的嵌入参数在每个 #embed 指令中最多只能出现一次。

平衡记号序列 会按在正常文本中进行处理以组成常量表达式,但不会对 defined__has_include__has_cpp_attribute__has_embed 表达式求值。

组成的常量表达式必须是具有非负值的整数常量表达式

  • 如果该常量表达式的值大于实现资源计数,那么资源计数依然是实现资源计数。
  • 否则资源计数就是该常量表达式的值。
constexpr unsigned char sound_signature[] =
{
// 某个能够展开为至少是个元素的假想资源
#embed <sdk/jump.wav> limit(2 + 2)
};
 
static_assert(sizeof(sound_signature) == 4);
 
// 等价于 to #embed <data.dat> limit(10)
#define DATA_LIMIT 10
#embed <data.dat> limit(DATA_LIMIT)
 
// 非良构
#embed <data.dat> limit(__has_include("a.h"))

[编辑] prefix 参数

形式为 prefix ( 平衡记号序列 (可选) ) 的嵌入参数在每个 #embed 指令中最多只能出现一次。

如果资源为空,那么忽略此嵌入参数。否则将平衡记号序列 放在紧接在逗号分隔的整数字面量列表之前的位置。

[编辑] suffix 参数

形式为 suffix ( 平衡记号序列 (可选) ) 的嵌入参数在每个 #embed 指令中最多只能出现一次。

如果资源为空,那么忽略此嵌入参数。否则将平衡记号序列 放在紧接在逗号分隔的整数字面量列表之后的位置。

constexpr unsigned char whl[] =
{
#embed "chess.glsl" \
    prefix(0xEF, 0xBB, 0xBF, ) /∗ 直接序列 ∗/ \
    suffix(,)
    0
};
 
// 始终空终止,并且在序列非空时包含该序列
 
constexpr bool is_empty = sizeof(whl) == 1 && whl[0] == '\0';
 
constexpr bool is_not_empty = sizeof(whl) >= 4
    && whl[sizeof(whl) - 1] == '\0'
    && whl[0] == '\xEF' && whl[1] == '\xBB' && whl[2] == '\xBF';
 
static_assert(is_empty || is_not_empty);

[编辑] if_empty 参数

形式为 if_empty ( 平衡记号序列 (可选) ) 的嵌入参数在每个 #embed 指令中最多只能出现一次。

如果资源为空,那么忽略此嵌入参数。否则以平衡记号序列 替换该 #embed 指令。

// 无论 /owo/uwurandom 有什么内容都会展开为 42203
#embed </owo/uwurandom> if_empty(42203) limit(0)

[编辑] 注解

功能特性测试宏 标准 功能特性
__cpp_pp_embed 202502L (C++26) #embed 指令

[编辑] 示例

演示 #embed 的效果。如果在翻译环境中可以将 data.dat 作为资源嵌入,那么以下程序中的所有断言都不会失败。

#include <cassert>
#include <cstddef>
#include <cstring>
#include <fstream>
#include <vector>
 
int main()
{
    constexpr unsigned char d[]
    {
#embed <data.dat>
    };
 
    const std::vector<unsigned char> vec_d
    {
#embed <data.dat>
    };
 
    constexpr std::size_t expected_size = sizeof(d);
    // 与在执行环境中嵌入的文件相同
    std::ifstream f_source("data.dat", std::ios_base::binary | std::ios_base::in);
    unsigned char runtime_d[expected_size];
 
    char* ifstream_ptr = reinterpret_cast<char*>(runtime_d);
    assert(!f_source.read(ifstream_ptr, expected_size));
 
    std::size_t ifstream_size = f_source.gcount();
    assert(ifstream_size == expected_size);
 
    int is_same = std::memcmp(&d[0], ifstream_ptr, ifstream_size);
    assert(is_same == 0);
 
    int is_same_vec = std::memcmp(vec_d.data(), ifstream_ptr, ifstream_size);
    assert(is_same_vec == 0);
}

[编辑] 引用

  • C++26 标准(ISO/IEC 14882:2026):
  • 15.4 Resource inclusion [cpp.embed]

[编辑] 参阅

二进制资源包含 (C23 起)C 文档