You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 22:32:00