C++文本嵌入二进制最佳实践

2025-03-07 23:57:42

# 前言

文本嵌入在编写着色器时特别有用。它能够使我们将着色器代码从项目代码中剥离出来,从而能够利用相应的lsp进行提示、高亮以及格式化,并且进一步降低代码耦合度。

对于Rust而言,嵌入一段字符串十分简单:

include!("path_to_file/file")

对于C++,一般以下有几种方法:

# 字符串

const char* vertex_shader_code =
    "#version 330 core\n"
    "layout (location = 0) in vec3 aPos;\n"
    "void main()\n"
    "{\n"
    "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
    "}";

这是最传统的方式,运行时初始化字符串。缺点很明显,性能较低,且修改困难。

# embed

embed是C的提案,但在C++中也得到支持:

const char vertex_shader_code[] = {
    #embed "path_to_file/file"
};

缺点在于目前只有clang得到支持,并且在编译时会显示警告:#embed is an extension of clang。在打印时,通过这种方式嵌入的字符会在末尾打印出未知字符(乱码),可以猜到以上数组并不包括字符串末尾的\0。但我目前没找到好的填\0方法。

另外,C++的std::embed还在日程中……

# xxd

在Linux下,有xxd命令,它提供了将一个文件转换为C语言头文件的功能,使用方法如下:

xxd -n variable_name -i input.glsl > output.h 

对于上面的例子,有:

xxd -n _vertex_shader_code -i ./assets/shader/vertex.glsl > ./include/shader/vertex.h 

它在对应位置生成如下的文件:

unsigned char _vertex_shader_code[] = {
  0x23, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x33, 0x33, 0x30,
  0x20, 0x63, 0x6f, 0x72, 0x65, 0x0a, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74,
  0x28, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20,
  0x30, 0x29, 0x20, 0x69, 0x6e, 0x20, 0x76, 0x65, 0x63, 0x33, 0x20, 0x61,
  0x50, 0x6f, 0x73, 0x3b, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61,
  0x69, 0x6e, 0x28, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x67,
  0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d,
  0x20, 0x76, 0x65, 0x63, 0x34, 0x28, 0x61, 0x50, 0x6f, 0x73, 0x2e, 0x78,
  0x2c, 0x20, 0x61, 0x50, 0x6f, 0x73, 0x2e, 0x79, 0x2c, 0x20, 0x61, 0x50,
  0x6f, 0x73, 0x2e, 0x7a, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x0a,
  0x7d, 0x0a
};
unsigned int _vertex_shader_code_len = 122;

可以注意到上面的数组并不是以\0结尾,如果需要转换到C字符串,则需要手动填\0。或是使用sed进行替换:

xxd -n _vertex_shader_code -i ./assets/shader/vertex.glsl | sed "s/}/,\'\\\0\'}/" > ./include/shader/vertex.h 

如果对代码格式化有强迫症,使用clang-format进行后处理格式化:

clang-format -i ./include/hierro/shader/*

然后在代码中include对应头文件:

#include "hierro/shader/vertex.h"

调用变量:

auto vertex_shader_code = (const char*)_vertex_shader_code;