能否通过C++宏从变量中的文件名生成xxd对应的标识符,实现资源的内存加载?
哎,这个问题我太懂了——之前也跟你一样头疼过xxd生成的标识符和文件名对应手动写的麻烦!先给你泼个小冷水:直接用宏从运行时的字符串变量生成xxd标识符是做不到的。
为啥呢?因为宏是在编译前的预处理阶段工作的,它只能处理编译期已知的字符串字面量,根本看不到你循环里那个str变量在运行时才会有的具体值。所以你想在循环里从str提取文件名和扩展名,再通过宏拼出image1_png这种标识符,这条路走不通。
不过别灰心,咱们可以换个思路:用宏帮你自动生成文件名到xxd标识符的映射表,这样就不用手动一个个写对应关系了,照样能保留你原来的循环逻辑。
给你举个具体的实现例子:
首先假设你的xxd工具生成的头文件里,每个资源都有这样的声明(这是xxd默认的输出格式,用xxd -i image1.png > image1.h就能生成):
extern const unsigned char image1_png[]; extern const unsigned int image1_png_len;
接下来我们用宏来自动构建映射表:
#include <unordered_map> #include <vector> #include <SFML/Graphics/Texture.hpp> // 定义一个宏,用来生成单个资源的映射条目 #define TEXTURE_ENTRY(filename) {filename, {filename##_png, filename##_png_len}} // 用宏批量生成所有纹理的映射表 const std::unordered_map<std::string, std::pair<const unsigned char*, unsigned int>> textureMap = { TEXTURE_ENTRY("image1.png"), TEXTURE_ENTRY("image2.png"), // 这里继续加你的其他纹理文件 }; // 如果需要保留原来的textureList,可以从映射表中提取 std::vector<std::string> textureList; for (const auto& entry : textureMap) { textureList.push_back(entry.first); } // 然后你的加载循环就可以改成这样 std::vector<sf::Texture> textures; for (const auto& str : textureList) { sf::Texture tx; // 从映射表中取出对应的内存数据和长度 const auto& resource = textureMap.at(str); tx.loadFromMemory(resource.first, resource.second); textures.push_back(tx); }
这个方法的好处是,你只需要写一遍TEXTURE_ENTRY("xxx.png"),宏会自动帮你把文件名和xxd生成的xxx_png、xxx_png_len关联起来,完全不用手动写每一组对应关系,省了超多重复劳动。
如果你的xxd生成的标识符命名规则不一样(比如改成了image1_png_data和image1_png_size),只需要调整宏里的拼接部分就行,把filename##_png改成filename##_png_data,filename##_png_len改成filename##_png_size就好。
要是你的资源文件名有规律,还可以用更复杂的预处理器技巧批量生成,但上面这个方法已经能覆盖大部分场景了,足够解决你的问题~
备注:内容来源于stack exchange,提问作者John Ziegler

