Namespaces
Variants

Resource inclusion (since C++26)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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

目录

语法

#embed < h-字符序列 > 预处理记号 换行 (1)
#embed " q-字符序列 " 预处理记号 换行 (2)
#embed 预处理记号 换行 (3)
__has_embed ( 平衡预处理记号 ) (4)
1) 搜索由 h-char-sequence 唯一标识的资源,并将该指令替换为资源的全部内容。
2) 搜索由 q-char-sequence 标识的资源,并用源文件的全部内容替换该指令。它可能回退到 (1) 的处理方式,将 q-char-sequence 视为资源标识符。
3) 若既不符合 (1) 也不符合 (2) pp-tokens 将进行宏替换。替换后的指令会再次尝试与 (1) (2) 匹配。
4) 检查资源是否可用于包含给定的 嵌入参数
new-line - 换行符
h-char-sequence - 一个或多个 h-char 组成的序列(参见 #include
q-char-sequence - 一个或多个 q-char 组成的序列(参见 #include
pp-tokens - 一个或多个 预处理记号 组成的序列
balanced-pp-tokens - 一个或多个预处理记号组成的序列,其中所有 ( [ { 都正确闭合

说明

1) 在多个位置序列中搜索由 h-char-sequence 唯一标识的资源,并将该指令替换为头文件的全部内容。具体如何指定这些位置或识别头文件由实现定义。
2) 导致该指令被 q-char-sequence  所标识资源的全部内容替换。命名资源的搜索方式由实现定义。
若此搜索不被支持,或搜索失败,该指令将被重新处理,如同其语法为 (1) 且包含与原始指令完全相同的序列(包括可能存在的 > 字符)。
3) 指令中 embed 之后的预处理令牌会像普通文本一样被处理(即每个当前被定义为宏名称的标识符会被其预处理令牌的替换列表所替代)。
若所有替换后产生的指令不符合前述两种形式之一,则行为未定义。
< > 预处理令牌对之间,或一对 " 字符之间的预处理令牌序列被组合成单个资源名称预处理令牌的方式是由实现定义的。
4) 使用语法 (3) #embed 指令,以 balanced-pp-tokens 作为其 pp-tokens ,搜索由该指令标识的资源。
  • 若该指令不满足 #embed 指令的语法要求,则程序非良构。
  • 若资源搜索成功且构造指令中的所有给定 embed参数 均受支持,则当资源非空时 __has_embed 表达式求值为 __STDC_EMBED_FOUND__ ,资源为空时求值为 __STDC_EMBED_EMPTY__
  • 否则, __has_embed 表达式求值为 __STDC_EMBED_NOT_FOUND__

资源

一个 资源 是指从翻译环境中可访问的数据源。资源具有 实现定义资源宽度 ,这是实现定义的资源位宽。若实现定义资源宽度不是 CHAR_BIT 的整数倍,则程序非良构。

implementation-resource-count 为 implementation-resource-width 除以 CHAR_BIT 。每个资源还具有一个 resource-count  ,该值默认为 implementation-resource-count,除非提供了 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) 中存在 pp-tokens ,其处理方式与普通文本相同。处理后的 pp-tokens 应构成一个 嵌入参数  序列,否则程序格式错误。嵌入参数具有以下语法:

limit ( balanced-pp-tokens ) (1)
prefix ( balanced-pp-tokens  (可选) ) (2)
suffix ( balanced-pp-tokens  (可选) ) (3)
if_empty ( balanced-pp-tokens  (可选) ) (4)
identifier :: identifier (5)
identifier :: identifier ( balanced-pp-tokens  (可选) ) (6)
1-4) 标准嵌入参数。
1) 限制待嵌入资源的资源数量。
2) 为嵌入的非空资源添加前缀。
3) 为嵌入的非空资源添加后缀。
4) 如果嵌入资源为空则替换该资源。
5,6) 非标准的嵌入参数。任何此类参数均为有条件支持,其语义由实现定义。

limit 参数

形式为 limit ( balanced-pp-tokens ) 的嵌入参数在每个 #embed 指令中最多只能出现一次。

balanced-pp-tokens 会像普通文本一样被处理以形成 常量表达式 ,但 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);
// 等价于 #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 ( balanced-pp-tokens  (可选) ) 的嵌入参数在每个 #embed 指令中最多只能出现一次。

如果资源为空,则忽略此嵌入参数。否则, balanced-pp-tokens 将紧置于逗号分隔的整数字面值列表之前。

suffix 参数

后缀形式的嵌入参数 suffix ( balanced-pp-tokens  (可选) ) 在每个 #embed 指令中最多只能出现一次。

如果资源为空,则忽略此嵌入参数。否则, balanced-pp-tokens 将紧跟在逗号分隔的整数字面值列表之后。

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 ( balanced-pp-tokens  (optional) ) 的嵌入参数在每个 #embed 指令中最多只能出现一次。

若资源 空,则忽略此嵌入参数。否则, #embed 指令将被 balanced-pp-tokens 替换。

// 无论 /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 资源嵌入 [cpp.embed]

参见

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