资源包含 (C++26 起)
#embed 是用于包含资源的预处理器指令。
语法
#embed < h字符序列 > 记号序列 换行
|
(1) | ||||||||
#embed " q字符序列 " 记号序列 换行
|
(2) | ||||||||
#embed 记号序列 换行
|
(3) | ||||||||
__has_embed ( 平衡记号序列 )
|
(4) | ||||||||
| 换行 | - | 换行字符 |
| h字符序列 | - | 一个或多个h字符 的序列(见 #include)
|
| q字符序列 | - | 一个或多个q字符 的序列(见 #include)
|
| 记号序列 | - | 一个或多个预处理记号的序列 |
| 平衡记号序列 | - | 一个或多个预处理记号的序列,其中每个 (、[ 和 { 都有正确关闭
|
解释
> 字符,如果存在)作为语法 (1) 中所需的序列。embed 后面的预处理记号会按在正常文本中进行处理(即每个目前定义为宏名的标识符都会被替换为它的预处理记号替换列表)。< 和 > 之间或一对 " 字符之前的预处理记号序列合并为单个资源预处理记号的方法由实现定义。#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) | ||||||||
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 文档
|