原始问题的时间安排可能是偶然的。事实证明,将来我建议的查找/加载资源方法也不会成为我们的选择(我之前使用的代码是跨平台的),所以我想出了一个解决方案为你工作。完整的代码作为 SO 答案发布有点多(但请参阅我的后续评论),尽管您可能很快就可以制作出自己的快速版本。
基本上,我编写了一个实用程序,它只将输出文件的基本名称作为argv[1] ("output-base-name"),而argv[2...(argc-1)] 是输入文件。我打开output-base-name.cpp 和output-base-name.h 进行写入(如果存在,将覆盖)。
对于每个输入文件,生成器实用程序都会这样做:
1:根据文件名(“generated_name”)生成一个变量名友好的标识符
2:读取输入文件
3:将以下内容转储到output-base-name.cpp:
static uint8_t generated_name[] = {
// Initialize with contents of input file as a bunch of 0xNN, 0xNN... values.
};
并入output-base-name.h,对应
static const unsigned IDR_generated_name = N; // start at 0, see below re:range check
4:在最后一个输入文件之后,结束 output-base-name.cpp 类似
static struct {
unsigned ID;
unsigned size; // I assume you aren't trying to embed files >4GB!
const uint8_t* data;
}
fileData[] = {
// one array element for each input file
{ IDR_generated_name, sizeof(generated_name), generated_name },
...
};
const uint8_t* getResourceData(unsigned ID){
// a real version would do a safer lookup/range check of some sort based on ID
return fileData[ID].data;
}
unsigned getResourceSize(unsigned ID){
// a real version would do a safer lookup/range check of some sort based on ID
return fileData[ID].size;
}
5:用原型完成 output-base-name.h,无论你的“get”函数是什么样的,例如
extern const uint8_t* getResourceData(unsigned ID);
extern unsigned getResourceSize(unsigned ID);
只需在需要访问数据的任何地方使用output-base-name.h 并将output-base-name.cpp 添加到您的makefile 或项目(使其依赖于二进制输入文件,使用构建规则中的生成器实用程序,make 将自动重新生成必要时为您服务)。如果您希望它们在某种动态库中,只需将适当的导出属性添加到您的“get”函数。
让生成器将你的输出包装在一个命名空间中,如果你愿意,可以将一个基于类的接口组合在一起(或者生成一个而不是示例“get”函数),你应该很好。
与使用未压缩的 zip 文件(甚至我建议用于 Windows 目标的嵌入式资源方法)相比,这种方法有几个优点:
- 在应用程序代码中处理起来要简单得多 - 无需读取 zip 目录、提取文件数据等。
- 不增加对其他第三方库的依赖来查找/提取文件数据(假设您不滚动自己的 zip 库,这将是更多工作)
- 所有解析和查找工作都已为您完成 - 如果将数据粘贴到静态库或可执行文件中,链接器已经解析了每个字节数组的地址,如果是共享库或 DLL,则由 OS 加载器解析
- 您知道数据将存在或您的二进制文件无法运行(使用 zip lib 方法,甚至 Windows 上的 Find/LoadResource 都不是这种情况)
- 与 zip 文件相比,Joe 用户更难以访问数据(因此更安全)
- 应该比 zip 或 Windows 资源方法更快,因为在应用程序的加载过程中预先解析了数据地址
- 它还可以用于任何形式的相对静态数据,您希望随应用程序一起提供(或提供给)应用程序 - 只需使用生成器将文件打包到共享/动态库中并使用生成的导出访问器函数即可。您甚至可以稍后(在编译/发布时间之后)创建更新的或全新的数据包,并使用配置选项在运行时加载。
- 如有必要,它将在较旧的 C++ 编译器中工作(或者,不生成任何命名空间或类包装器,甚至是普通的旧 C)
这只是我使用的方法的一个示例,但它涵盖了所有基础知识,因此,如果早点而不是晚点(再次请参阅评论)解决方案对您来说更好,这可能就足够了(您想要范围检查“get”函数中的值 - 至少在调试版本中,可能使用某种容器而不是使用 c 样式的结构数组等)。