如何用std::map关联OpenWeatherMap图标字符串与u8g2的unsigned char数组?
我正在用Arduino IDE(C++11)开发ESP32项目,通过openweathermap.org API获取天气图标标识,再用u8g2库驱动OLED显示屏显示对应的XBM格式图标。
OpenWeatherMap返回的天气图标标识是const char*类型,比如"01d"、"01n"等,共16种。我已经将所有图标以XBM格式定义为unsigned char数组:
static unsigned char icon01d[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ... 剩余数据 };
显示图标的代码是:
u8g2.drawXBMP(96, 0, 32, 32, icon01d);
我想通过std::map把API返回的字符串和图标数组关联起来,预期的写法是:
std::map<std::string, unsigned char[]*> WEATHER_ICONS = { {"01d", &icon01d}, {"01n", &icon01n}, // ... 其他图标映射 };
然后通过以下方式调用显示:
u8g2.drawXBMP(96, 0, 32, 32, *WEATHER_ICONS[weatherIcon]);
其中weatherIcon是转换为std::string的API返回标识。
但我尝试了多种map的value类型,都出现错误:
| 使用的变量类型 | 返回的错误信息 |
|---|---|
unsigned char[128]* | template argument 2 is invalid |
unsigned char[]* | template argument 2 is invalid |
unsigned char*[128] | could not convert '{{"01d", (& icon01d)}, {"01n", (& icon01n)}}' from '<brace-enclosed initializer list>' to 'std::map<std::__cxx11::basic_string<char>, unsigned char* [128]>' |
unsigned char*[] | could not convert '{{"01d", (& icon01d)}, {"01n", (& icon01n)}}' from '<brace-enclosed initializer list>' to 'std::map<std::__cxx11::basic_string<char>, unsigned char* []>' |
unsigned char** | could not convert '{{"01d", (& icon01d)}, {"01n", (& icon01n)}}' from '<brace-enclosed initializer list>' to 'std::map<std::__cxx11::basic_string<char>, unsigned char**>' |
现在我用一堆if-else做临时解决:
if (strcmp(weatherIcon, "01d") == 0) u8g2.drawXBMP(96, 0, 32, 32, icon01d); else if(strcmp(weatherIcon, "01n") == 0) u8g2.drawXBMP(96, 0, 32, 32, icon01n); else if(strcmp(weatherIcon, "02d") == 0) u8g2.drawXBMP(96, 0, 32, 32, icon02d); else if(strcmp(weatherIcon, "02n") == 0) u8g2.drawXBMP(96, 0, 32, 32, icon02n); else if(strcmp(weatherIcon, "03d") == 0) u8g2.drawXBMP(96, 0, 32, 32, icon03d); else if(strcmp(weatherIcon, "03n") == 0) u8g2.drawXBMP(96, 0, 32, 32, icon03n); else if(strcmp(weatherIcon, "04d") == 0) u8g2.drawXBMP(96, 0, 32, 32, icon04d); else if(strcmp(weatherIcon, "04n") == 0) u8g2.drawXBMP(96, 0, 32, 32, icon04n); else if(strcmp(weatherIcon, "09d") == 0) u8g2.drawXBMP(96, 0, 32, 32, icon09d); else if(strcmp(weatherIcon, "09n") == 0) u8g2.drawXBMP(96, 0, 32, 32, icon09n); else if(strcmp(weatherIcon, "11d") == 0) u8g2.drawXBMP(96, 0, 32, 32, icon11d); else if(strcmp(weatherIcon, "11n") == 0) u8g2.drawXBMP(96, 0, 32, 32, icon11n); else if(strcmp(weatherIcon, "13d") == 0) u8g2.drawXBMP(96, 0, 32, 32, icon13d); else if(strcmp(weatherIcon, "13n") == 0) u8g2.drawXBMP(96, 0, 32, 32, icon13n); else if(strcmp(weatherIcon, "50d") == 0) u8g2.drawXBMP(96, 0, 32, 32, icon50d); else if(strcmp(weatherIcon, "50n") == 0) u8g2.drawXBMP(96, 0, 32, 32, icon50n);
虽然能用,但不够优雅,求正确的解决方案。
问题核心是C++中数组类型无法直接作为std::map的value类型,数组会退化为指针,但直接用数组地址初始化map会出现类型不匹配。可以用**unsigned char*作为map的value类型**,因为数组名本身可隐式转换为指向首元素的指针,正好匹配drawXBMP的参数要求。
1. 定义正确的std::map
#include <map> #include <string> // 先确保所有图标数组已定义 static unsigned char icon01d[] = { /* ... */ }; static unsigned char icon01n[] = { /* ... */ }; // ... 其他图标 std::map<std::string, unsigned char*> WEATHER_ICONS = { {"01d", icon01d}, {"01n", icon01n}, {"02d", icon02d}, {"02n", icon02n}, {"03d", icon03d}, {"03n", icon03n}, {"04d", icon04d}, {"04n", icon04n}, {"09d", icon09d}, {"09n", icon09n}, {"11d", icon11d}, {"11n", icon11n}, {"13d", icon13d}, {"13n", icon13n}, {"50d", icon50d}, {"50n", icon50n} };
这里不需要取数组地址&icon01d,数组名icon01d会自动转换为unsigned char*类型,和drawXBMP的参数类型完全匹配。
2. 调用显示图标
如果weatherIcon是std::string类型:
// 先检查map中是否存在该标识,避免访问不存在的键 auto it = WEATHER_ICONS.find(weatherIcon); if (it != WEATHER_ICONS.end()) { u8g2.drawXBMP(96, 0, 32, 32, it->second); } else { // 处理未知图标的情况,比如显示默认图标 // u8g2.drawXBMP(96, 0, 32, 32, defaultIcon); }
如果weatherIcon是const char*类型,可直接传入find:
auto it = WEATHER_ICONS.find(weatherIcon);
为什么之前的尝试失败?
unsigned char[]*或unsigned char[128]*:C++不允许将数组类型的指针作为模板参数,数组大小是类型的一部分,无法作为通用模板参数。unsigned char*[128]:这是指向128个unsigned char*的数组类型,和图标数组类型不匹配。unsigned char**:&icon01d是指向数组的指针(unsigned char (*)[128]),无法隐式转换为指向指针的指针(unsigned char**),二者类型完全不同。
额外优化:编译期映射(适合ESP32)
如果编译器支持C++11及以上,可使用constexpr结合std::array实现编译期映射,避免std::map的运行时开销,更适合资源有限的ESP32设备:
#include <array> #include <string_view> // 定义映射结构体 struct IconMap { std::string_view key; unsigned char* value; }; constexpr std::array<IconMap, 16> WEATHER_ICONS = {{ {"01d", icon01d}, {"01n", icon01n}, // ... 其他映射 }}; // 查找函数 unsigned char* findIcon(const char* key) { for (const auto& pair : WEATHER_ICONS) { if (pair.key == key) { return pair.value; } } return nullptr; // 返回空指针表示未找到 } // 调用 unsigned char* icon = findIcon(weatherIcon); if (icon != nullptr) { u8g2.drawXBMP(96, 0, 32, 32, icon); }
内容的提问来源于stack exchange,提问作者HenkJan van der Pol

