diff --git a/Light.hpp b/Light.hpp index e73db33..837f403 100644 --- a/Light.hpp +++ b/Light.hpp @@ -7,39 +7,38 @@ #include "config.h" #include "utils.h" -class Light { -public: - virtual CRGB *data() = 0; - virtual int count() = 0; -}; +// ==================== LightStrip ==================== template -class LightStrip : public Light { -private: - CRGB leds[COUNT]; - +class LightStrip { public: - CRGB *data() override { - return leds; + static constexpr int count() { + return COUNT; } - int count() override { - return COUNT; +private: + CRGB leds[count()]; +public: + CRGB* data() { + return this->leds; } - int l() { +public: + constexpr int l() const { return COUNT; } - CRGB &at(int i) { + CRGB& at(int i) { if (REVERSE) { - return leds[l() - i - 1]; + return this->leds[l() - i - 1]; } else { - return leds[i]; + return this->leds[i]; } } }; +// ==================== LightPanel ==================== + enum LightPanelArrangement { Z_WORD = 0x0, // Z 字形 SNAKE = 0x1, // 蛇形 @@ -50,28 +49,29 @@ enum LightPanelArrangement { }; template -class LightPanel : public Light { -private: - CRGB leds[X_COUNT * Y_COUNT]; - +class LightPanel { public: - CRGB *data() override { - return leds; + static constexpr int count() { + return X_COUNT * Y_COUNT; } - int count() override { - return X_COUNT * Y_COUNT; +private: + CRGB leds[count()]; +public: + CRGB* data() { + return this->leds; } - int w() { +public: + constexpr int w() const { return X_COUNT; } - int h() { + constexpr int h() const { return Y_COUNT; } - CRGB &at(int x, int y) { + CRGB& at(int x, int y) { int _w = w(); int _h = h(); if (ARRANGEMENT & VERTICAL) { @@ -89,10 +89,12 @@ class LightPanel : public Light { if (ARRANGEMENT & FLIP) { y = _h - y - 1; } - return leds[y * _w + x]; + return this->leds[y * _w + x]; } }; +// ==================== LightDisc ==================== + enum LightDiscArrangement { CLOCKWISE = 0x0, // 顺时针 ANTICLOCKWISE = 0x1, // 逆时针 @@ -101,71 +103,78 @@ enum LightDiscArrangement { }; template -class LightDisc : public Light { +class LightDisc { private: - static constexpr int led_ring_count = sizeof...(COUNT_PER_RING); - static constexpr int led_rings[led_ring_count] = {COUNT_PER_RING...}; - static constexpr int led_count = sum(led_rings, led_rings + led_ring_count, 0); - const int rings[led_ring_count] = {COUNT_PER_RING...}; - CRGB leds[led_count]; + static constexpr int ring_count() { + return sizeof...(COUNT_PER_RING); + } + static constexpr int rings[ring_count()] = {COUNT_PER_RING...}; public: - CRGB *data() override { - return leds; + static constexpr int count() { + return sum(rings, rings + ring_count(), 0); } - int count() override { - return led_count; +private: + CRGB leds[count()]; +public: + CRGB* data() { + return this->leds; } - int r() { - return led_ring_count; +public: + constexpr int r() const { + return ring_count(); } - int l(int ring) { + constexpr int l(int ring) const { return rings[ring]; } - CRGB &at(int ring, int i) { + CRGB& at(int ring, int i) { if (ARRANGEMENT & ANTICLOCKWISE) { i = l(ring) - i - 1; } if (ARRANGEMENT & INSIDE_OUT) { - return leds[sum(rings + ring + 1, rings + r(), i)]; + return this->leds[sum(rings + ring + 1, rings + r(), 0) + i]; } else { - return leds[sum(rings, rings + ring, i)]; + return this->leds[sum(rings, rings + ring, 0) + i]; } } }; -template -class LightCube : public Light { -private: - CRGB leds[X_COUNT * Y_COUNT * Z_COUNT]; +template +constexpr int LightDisc::rings[]; -public: - CRGB *data() override { - return leds; - } +// ==================== LightCube ==================== - int count() override { +template +class LightCube { +public: + static constexpr int count() { return X_COUNT * Y_COUNT * Z_COUNT; } - int l() { +private: + CRGB leds[count()]; +public: + CRGB* data() { return this->leds; } + +public: + constexpr int l() const { return X_COUNT; } - int w() { + constexpr int w() const { return Y_COUNT; } - int h() { + constexpr int h() const { return Z_COUNT; } - CRGB &at(int x, int y, int z) { - return leds[z * l() * w() + y * l() + x]; + CRGB& at(int x, int y, int z) { + return this->leds[z * l() * w() + y * l() + x]; } }; diff --git a/LightEffect.hpp b/LightEffect.hpp index 811d243..5f67e79 100644 --- a/LightEffect.hpp +++ b/LightEffect.hpp @@ -5,8 +5,10 @@ #include #include #include +#include #include "Light.hpp" +#include "any.h" #include "utils.h" enum EffectType { @@ -40,17 +42,59 @@ const char* effect2str(EffectType effect); extern const uint16_t &fps; +template class Effect { +private: + std::any _impl; + std::function _type; + std::function _update; + std::function _writeToJSON; + public: - virtual EffectType type() = 0; - virtual bool update(Light &light, uint32_t deltaTime) = 0; - virtual void writeToJSON(JsonDocument &json) { json["mode"] = type(); }; - template - static Effect* readFromJSON(JsonDocument &json); + Effect() noexcept {} + + template + Effect(T &&impl) : _impl(std::forward(impl)) { + _type = [this]() -> EffectType { + return std::any_cast(_impl).type(); + }; + _update = [this](Light &light, uint32_t deltaTime) -> bool { + return std::any_cast(_impl).update(light, deltaTime); + }; + _writeToJSON = [this](JsonDocument &json) { + std::any_cast(_impl).writeToJSON(json); + }; + } + + template + Effect& operator=(T &&impl) { + ::new(this) Effect(std::forward(impl)); + return *this; + } + + template + T& as() { + return std::any_cast(_impl); + } + + EffectType type() const { + return _type(); + } + + bool update(Light &light, uint32_t deltaTime) { + return _update(light, deltaTime); + } + + void writeToJSON(JsonDocument &json) const { + json["mode"] = type(); + _writeToJSON(json); + } + + // defined at the end of the file + static Effect readFromJSON(JsonDocument &json); }; -template -class ConstantEffect : public Effect { +class ConstantEffect { private: bool updated; CRGB currentColor; @@ -59,11 +103,12 @@ class ConstantEffect : public Effect { ConstantEffect(uint32_t color) : updated(false), currentColor(color) {} - EffectType type() override { + EffectType type() const { return CONSTANT; } - bool update(Light &light, uint32_t deltaTime) override { + template + bool update(Light &light, uint32_t deltaTime) { if (!updated) { fill_solid(light.data(), light.count(), currentColor); updated = true; @@ -72,19 +117,17 @@ class ConstantEffect : public Effect { return false; } - void writeToJSON(JsonDocument &json) override { - Effect::writeToJSON(json); + void writeToJSON(JsonDocument &json) const { json["color"] = rgb2hex(currentColor.r, currentColor.g, currentColor.b); } - static ConstantEffect* readFromJSON(JsonDocument &json) { + static ConstantEffect readFromJSON(JsonDocument &json) { uint32_t color = json["color"]; - return new ConstantEffect(color); + return ConstantEffect(color); } }; -template -class BlinkEffect : public Effect { +class BlinkEffect { private: uint16_t currentFrame; CRGB currentColor; @@ -95,11 +138,12 @@ class BlinkEffect : public Effect { BlinkEffect(uint32_t color, float lastTime, float interval) : currentFrame(0), currentColor(color), lastTime(lastTime), interval(interval) {} - EffectType type() override { + EffectType type() const { return BLINK; } - bool update(Light &light, uint32_t deltaTime) override { + template + bool update(Light &light, uint32_t deltaTime) { int lastTime = fps * this->lastTime; int interval = fps * this->interval; bool needUpdate = false; @@ -116,23 +160,21 @@ class BlinkEffect : public Effect { return needUpdate; } - void writeToJSON(JsonDocument &json) override { - Effect::writeToJSON(json); + void writeToJSON(JsonDocument &json) const { json["color"] = rgb2hex(currentColor.r, currentColor.g, currentColor.b); json["lastTime"] = lastTime; json["interval"] = interval; } - static BlinkEffect* readFromJSON(JsonDocument &json) { + static BlinkEffect readFromJSON(JsonDocument &json) { uint32_t color = json["color"]; float lastTime = json["lastTime"]; float interval = json["interval"]; - return new BlinkEffect(color, lastTime, interval); + return BlinkEffect(color, lastTime, interval); } }; -template -class BreathEffect : public Effect { +class BreathEffect { private: uint16_t currentFrame; CRGB currentColor; @@ -143,11 +185,12 @@ class BreathEffect : public Effect { BreathEffect(uint32_t color, float lastTime, float interval) : currentFrame(0), currentColor(color), lastTime(lastTime), interval(interval) {} - EffectType type() override { + EffectType type() const { return BREATH; } - bool update(Light &light, uint32_t deltaTime) override { + template + bool update(Light &light, uint32_t deltaTime) { int lastTime = fps * this->lastTime; int interval = fps * this->interval; bool needUpdate = false; @@ -165,23 +208,21 @@ class BreathEffect : public Effect { return needUpdate; } - void writeToJSON(JsonDocument &json) override { - Effect::writeToJSON(json); + void writeToJSON(JsonDocument &json) const { json["color"] = rgb2hex(currentColor.r, currentColor.g, currentColor.b); json["lastTime"] = lastTime; json["interval"] = interval; } - static BreathEffect* readFromJSON(JsonDocument &json) { + static BreathEffect readFromJSON(JsonDocument &json) { uint32_t color = json["color"]; float lastTime = json["lastTime"]; float interval = json["interval"]; - return new BreathEffect(color, lastTime, interval); + return BreathEffect(color, lastTime, interval); } }; -template -class ChaseEffect : public Effect { +class ChaseEffect { private: uint16_t currentFrame; CRGB currentColor; @@ -192,14 +233,10 @@ class ChaseEffect : public Effect { ChaseEffect(uint32_t color, uint8_t direction, float lastTime) : currentFrame(0), currentColor(color), direction(direction), lastTime(lastTime) {} - EffectType type() override { + EffectType type() const { return CHASE; } - bool update(Light &light, uint32_t deltaTime) override { - return update(static_cast(light), deltaTime); - } - template bool update(LightStrip &light, uint32_t deltaTime) { int lastTime = fps * this->lastTime; @@ -242,23 +279,21 @@ class ChaseEffect : public Effect { return needUpdate; } - void writeToJSON(JsonDocument &json) override { - Effect::writeToJSON(json); + void writeToJSON(JsonDocument &json) const { json["color"] = rgb2hex(currentColor.r, currentColor.g, currentColor.b); json["direction"] = direction; json["lastTime"] = lastTime; } - static ChaseEffect* readFromJSON(JsonDocument &json) { + static ChaseEffect readFromJSON(JsonDocument &json) { uint32_t color = json["color"]; uint8_t direction = json["direction"]; float lastTime = json["lastTime"]; - return new ChaseEffect(color, direction, lastTime); + return ChaseEffect(color, direction, lastTime); } }; -template -class RainbowEffect : public Effect { +class RainbowEffect { private: uint8_t currentHue; int8_t delta; @@ -267,11 +302,12 @@ class RainbowEffect : public Effect { RainbowEffect(int8_t delta) : currentHue(0), delta(delta) {} - EffectType type() override { + EffectType type() const { return RAINBOW; } - bool update(Light &light, uint32_t deltaTime) override { + template + bool update(Light &light, uint32_t deltaTime) { CHSV hsv(currentHue, 255, 240); CRGB rgb; hsv2rgb_rainbow(hsv, rgb); @@ -280,19 +316,17 @@ class RainbowEffect : public Effect { return true; } - void writeToJSON(JsonDocument &json) override { - Effect::writeToJSON(json); + void writeToJSON(JsonDocument &json) const { json["delta"] = delta; } - static RainbowEffect* readFromJSON(JsonDocument &json) { + static RainbowEffect readFromJSON(JsonDocument &json) { uint8_t delta = json["delta"]; - return new RainbowEffect(delta); + return RainbowEffect(delta); } }; -template -class StreamEffect : public Effect { +class StreamEffect { private: uint8_t currentHue; uint8_t direction; @@ -302,14 +336,10 @@ class StreamEffect : public Effect { StreamEffect(uint8_t direction, int8_t delta) : currentHue(0), direction(direction), delta(delta) {} - EffectType type() override { + EffectType type() const { return STREAM; } - bool update(Light &light, uint32_t deltaTime) override { - return update(static_cast(light), deltaTime); - } - template bool update(LightStrip &light, uint32_t deltaTime) { fill_rainbow(light.data(), light.count(), currentHue); @@ -330,21 +360,19 @@ class StreamEffect : public Effect { return true; } - void writeToJSON(JsonDocument &json) override { - Effect::writeToJSON(json); + void writeToJSON(JsonDocument &json) const { json["direction"] = direction; json["delta"] = delta; } - static StreamEffect* readFromJSON(JsonDocument &json) { + static StreamEffect readFromJSON(JsonDocument &json) { uint8_t direction = json["direction"]; uint8_t delta = json["delta"]; - return new StreamEffect(direction, delta); + return StreamEffect(direction, delta); } }; -template -class AnimationEffect : public Effect { +class AnimationEffect { private: String animName; File file; @@ -375,11 +403,12 @@ class AnimationEffect : public Effect { } } - EffectType type() override { + EffectType type() const { return ANIMATION; } - bool update(Light &light, uint32_t deltaTime) override { + template + bool update(Light &light, uint32_t deltaTime) { if (!file) { return false; } @@ -423,19 +452,17 @@ class AnimationEffect : public Effect { return true; } - void writeToJSON(JsonDocument &json) override { - Effect::writeToJSON(json); + void writeToJSON(JsonDocument &json) const { json["animName"] = animName; } - static AnimationEffect* readFromJSON(JsonDocument &json) { + static AnimationEffect readFromJSON(JsonDocument &json) { const char *animName = json["animName"]; - return new AnimationEffect(animName); + return AnimationEffect(animName); } }; -template -class MusicEffect : public Effect { +class MusicEffect { private: uint8_t soundMode; // 0-电平模式 1-频谱模式 uint8_t currentHue; @@ -449,14 +476,10 @@ class MusicEffect : public Effect { currentVolume = volume; } - EffectType type() override { + EffectType type() const { return MUSIC; } - bool update(Light &light, uint32_t deltaTime) override { - return update(static_cast(light), deltaTime); - } - template bool update(LightStrip &light, uint32_t deltaTime) { if (soundMode == 0) { @@ -508,19 +531,17 @@ class MusicEffect : public Effect { return true; } - void writeToJSON(JsonDocument &json) override { - Effect::writeToJSON(json); + void writeToJSON(JsonDocument &json) const { json["soundMode"] = soundMode; } - static MusicEffect* readFromJSON(JsonDocument &json) { + static MusicEffect readFromJSON(JsonDocument &json) { uint8_t soundMode = json["soundMode"]; - return new MusicEffect(soundMode); + return MusicEffect(soundMode); } }; -template -class CustomEffect : public Effect { +class CustomEffect { private: int index; @@ -531,49 +552,49 @@ class CustomEffect : public Effect { return index; } - EffectType type() override { + EffectType type() const { return CUSTOM; } - bool update(Light &light, uint32_t deltaTime) override { + template + bool update(Light &light, uint32_t deltaTime) { return true; } - void writeToJSON(JsonDocument &json) override { - Effect::writeToJSON(json); + void writeToJSON(JsonDocument &json) const { } - static CustomEffect* readFromJSON(JsonDocument &json) { - return new CustomEffect(); + static CustomEffect readFromJSON(JsonDocument &json) { + return CustomEffect(); } }; -template -Effect* Effect::readFromJSON(JsonDocument &json) { +template +Effect Effect::readFromJSON(JsonDocument &json) { if (json.containsKey("mode")) { EffectType mode = json["mode"].as(); switch (mode) { case CONSTANT: - return ConstantEffect::readFromJSON(json); + return ConstantEffect::readFromJSON(json); case BLINK: - return BlinkEffect::readFromJSON(json); + return BlinkEffect::readFromJSON(json); case BREATH: - return BreathEffect::readFromJSON(json); + return BreathEffect::readFromJSON(json); case CHASE: - return ChaseEffect::readFromJSON(json); + return ChaseEffect::readFromJSON(json); case RAINBOW: - return RainbowEffect::readFromJSON(json); + return RainbowEffect::readFromJSON(json); case STREAM: - return StreamEffect::readFromJSON(json); + return StreamEffect::readFromJSON(json); case ANIMATION: - return AnimationEffect::readFromJSON(json); + return AnimationEffect::readFromJSON(json); case MUSIC: - return MusicEffect::readFromJSON(json); + return MusicEffect::readFromJSON(json); case CUSTOM: - return CustomEffect::readFromJSON(json); + return CustomEffect::readFromJSON(json); } } - return new ConstantEffect(DEFAULT_COLOR); // 默认为常亮 + return ConstantEffect(DEFAULT_COLOR); // 默认为常亮 } #endif // __LIGHTEFFECT_HPP__ diff --git a/RGBLight.ino b/RGBLight.ino index cf65c4e..8190a2f 100644 --- a/RGBLight.ino +++ b/RGBLight.ino @@ -1,25 +1,26 @@ /** * RGB Light 炫酷 RGB 灯 * - * 基于 ESP8266 使用 Arduino 开发的物联网炫酷小彩灯, 支持多种形态多种光效, 配套自研网页/小程序/PC 客户端 + * 基于 ESP8266 使用 Arduino 开发的物联网炫酷小彩灯, 支持多种形态多种光效, + * 配套自研网页/小程序/PC 客户端 * * @author QingChenW */ #include "config.h" -#include #include -#include -#include -#include +#include #include +#include #include #include -#include -#include +#include +#include +#include #include -#include +#include +#include #define FASTLED_ESP8266_RAW_PIN_ORDER // 不需要转换 #include #ifdef ENABLE_DEBUG @@ -33,17 +34,16 @@ #define MIME_TYPE(t) (mime::mimeTable[mime::type::t].mimeType) -typedef std::function CreateEffectFunc; - const char *product_name = "rgblight"; const char *model_name = MODEL; const char *version = VERSION; const uint32_t version_code = VERSION_CODE; +typedef std::function(int argc, const char *argv[])> CreateEffectFunc; CreateEffectFunc effectFactories[EFFECT_TYPE_COUNT]; Ticker timer; LIGHT_TYPE light; -Effect *lightEffect; +Effect lightEffect; DNSServer dnsServer; ESP8266WebServer webServer(80); WebSocketsServer wsServer(81); @@ -77,7 +77,7 @@ void serializeSettings(JsonDocument &doc) { doc["refreshRate"] = config.refreshRate; doc["brightness"] = config.brightness; doc["temperature"] = config.temperature; - lightEffect->writeToJSON(doc); + lightEffect.writeToJSON(doc); } void saveSettings() { @@ -111,7 +111,8 @@ void readSettings() { } } if (doc["version"] != version_code) { - Serial.printf_P(PSTR("Update settings from %d to %d\n"), doc["version"].as(), version_code); + Serial.printf_P(PSTR("Update settings from %d to %d\n"), + doc["version"].as(), version_code); shouldSave = true; } config.name = doc["name"] | NAME; @@ -121,12 +122,12 @@ void readSettings() { config.refreshRate = doc["refreshRate"] | 60; config.brightness = doc["brightness"] | 63; config.temperature = doc["temperature"] | 6600; - lightEffect = Effect::readFromJSON(doc); + lightEffect = Effect::readFromJSON(doc); FastLED.setBrightness(config.brightness); FastLED.setTemperature(CRGB(kelvin2rgb(config.temperature))); timer.attach_ms(1000 / config.refreshRate, updateLight); - + if (shouldSave) { saveSettings(); } @@ -142,8 +143,10 @@ int scanWifi(JsonArray &array) { obj["ssid"] = WiFi.SSID(i); obj["rssi"] = WiFi.RSSI(i); obj["type"] = WiFi.encryptionType(i); - Serial.printf_P(PSTR("%d: %s (%d)%c\n"), i + 1, WiFi.SSID(i).c_str(), WiFi.RSSI(i), - (WiFi.encryptionType(i) == ENC_TYPE_NONE) ? ' ' : '*'); + Serial.printf_P(PSTR("%d: %s (%d)%c\n"), i + 1, + WiFi.SSID(i).c_str(), WiFi.RSSI(i), + (WiFi.encryptionType(i) == ENC_TYPE_NONE) ? ' ' + : '*'); } } else { Serial.println(F("No wifi found")); @@ -188,22 +191,23 @@ bool startHotspot() { } void updateLight() { - if (lightEffect->update(light, 0)) { + if (lightEffect.update(light, 0)) { FastLED.show(); // delayMicroseconds(100); } } void handleCommand(SenderFunc sender, char *line) { - if (lightEffect->type() == MUSIC) { - if (!isalpha(line[0])) { // 假定所有命令都是字母开头且以字母开头的一定是命令 - ((MusicEffect *) lightEffect)->setVolume(atof(line)); + if (lightEffect.type() == MUSIC) { + if (!isalpha( + line[0])) { // 假定所有命令都是字母开头且以字母开头的一定是命令 + lightEffect.as().setVolume(atof(line)); return; } - } else if (lightEffect->type() == CUSTOM) { + } else if (lightEffect.type() == CUSTOM) { if (!isalpha(line[0])) { uint32_t color = str2hex(line); - int &index = ((CustomEffect *) lightEffect)->getIndex(); + int &index = lightEffect.as().getIndex(); light.data()[index++] = CRGB(color); if (index >= light.count()) { index = 0; @@ -217,45 +221,45 @@ void handleCommand(SenderFunc sender, char *line) { void initEffects() { effectFactories[CONSTANT] = [](int argc, const char *argv[]) { uint32_t color = argc > 0 ? str2hex(argv[0]) : DEFAULT_COLOR; - return new ConstantEffect(color); + return ConstantEffect(color); }; effectFactories[BLINK] = [](int argc, const char *argv[]) { uint32_t color = argc > 0 ? str2hex(argv[0]) : DEFAULT_COLOR; float lastTime = argc > 1 ? atof(argv[1]) : 1.0; float interval = argc > 2 ? atof(argv[2]) : 1.0; - return new BlinkEffect(color, lastTime, interval); + return BlinkEffect(color, lastTime, interval); }; effectFactories[BREATH] = [](int argc, const char *argv[]) { uint32_t color = argc > 0 ? str2hex(argv[0]) : DEFAULT_COLOR; float lastTime = argc > 1 ? atof(argv[1]) : 1.0; float interval = argc > 2 ? atof(argv[2]) : 0.5; - return new BreathEffect(color, lastTime, interval); + return BreathEffect(color, lastTime, interval); }; effectFactories[CHASE] = [](int argc, const char *argv[]) { - uint32_t color = argc > 0 ? str2hex(argv[0]) : DEFAULT_COLOR; + uint32_t color = argc > 0 ? str2hex(argv[0]) : DEFAULT_COLOR; uint8_t direction = argc > 1 ? atoi(argv[1]) : 0; - float lastTime = argc > 2 ? atof(argv[2]) : 0.2; - return new ChaseEffect(color, direction, lastTime); + float lastTime = argc > 2 ? atof(argv[2]) : 0.2; + return ChaseEffect(color, direction, lastTime); }; effectFactories[RAINBOW] = [](int argc, const char *argv[]) { int8_t delta = argc > 0 ? atoi(argv[0]) : 1; - return new RainbowEffect(delta); + return RainbowEffect(delta); }; effectFactories[STREAM] = [](int argc, const char *argv[]) { uint8_t direction = argc > 0 ? atoi(argv[0]) : 0; - int8_t delta = argc > 1 ? atoi(argv[1]) : 1; - return new StreamEffect(direction, delta); + int8_t delta = argc > 1 ? atoi(argv[1]) : 1; + return StreamEffect(direction, delta); }; effectFactories[ANIMATION] = [](int argc, const char *argv[]) { const char *name = argc > 0 ? argv[0] : ""; - return new AnimationEffect(name); + return AnimationEffect(name); }; effectFactories[MUSIC] = [](int argc, const char *argv[]) { uint8_t mode = argc > 0 ? atoi(argv[0]) : 1; - return new MusicEffect(mode); + return MusicEffect(mode); }; effectFactories[CUSTOM] = [](int argc, const char *argv[]) { - return new CustomEffect(); + return CustomEffect(); }; } @@ -263,188 +267,211 @@ void registerCommands() { cmdHandler.setDefaultHandler([](SenderFunc sender, int argc, char *argv[]) { sender("Unknown command. type 'help' for helps."); }); - cmdHandler.registerCommand("help", "Show command helps", [](SenderFunc sender, int argc, char *argv[]) { - cmdHandler.printHelp(sender); - }); + cmdHandler.registerCommand("help", "Show command helps", + [](SenderFunc sender, int argc, char *argv[]) { + cmdHandler.printHelp(sender); + }); #ifdef ENABLE_DEBUG - cmdHandler.registerCommand("debug", "Show debug info", [](SenderFunc sender, int argc, char *argv[]) { - printSystemInfo(); - printWifiInfo(); - }); + cmdHandler.registerCommand("debug", "Show debug info", + [](SenderFunc sender, int argc, char *argv[]) { + printSystemInfo(); + printWifiInfo(); + }); #endif - cmdHandler.registerCommand("version", "Show version", [](SenderFunc sender, int argc, char *argv[]) { - StaticJsonDocument<256> doc; - doc["product"] = product_name; - doc["model"] = model_name; - doc["id"] = ESP.getChipId(); - doc["version"] = version; - doc["versionCode"] = version_code; - doc["sdkVersion"] = ESP.getFullVersion(); - String str; - serializeJson(doc, str); - sender(str.c_str()); - }); - cmdHandler.registerCommand("status", "Show status", [](SenderFunc sender, int argc, char *argv[]) { - StaticJsonDocument<256> doc; - doc["vcc"] = ESP.getVcc() / 1000.0; - doc["resetReason"] = ESP.getResetReason(); - doc["freeHeap"] = ESP.getFreeHeap(); - doc["heapFragment"] = ESP.getHeapFragmentation(); - doc["maxFreeBlock"] = ESP.getMaxFreeBlockSize(); - doc["RSSI"] = WiFi.RSSI(); - FSInfo fs_info; - LittleFS.info(fs_info); - doc["fsTotalSpace"] = fs_info.totalBytes; - doc["fsUsedSpace"] = fs_info.usedBytes; - String str; - serializeJson(doc, str); - sender(str.c_str()); - }); - cmdHandler.registerCommand("config", "Get config", [](SenderFunc sender, int argc, char *argv[]) { - StaticJsonDocument<1024> doc; - if (WiFi.getMode() == WIFI_AP) { - struct ip_info info; - wifi_get_ip_info(SOFTAP_IF, &info); - doc["ip"] = IPAddress(info.ip.addr).toString(); - doc["mask"] = IPAddress(info.netmask.addr).toString(); - doc["gateway"] = IPAddress(info.gw.addr).toString(); - } else { - doc["ip"] = WiFi.localIP().toString(); - doc["mask"] = WiFi.subnetMask().toString(); - doc["gateway"] = WiFi.gatewayIP().toString(); - } - serializeSettings(doc); - String str; - serializeJson(doc, str); - sender(str.c_str()); - }); - cmdHandler.registerCommand("scan", "Scan wifi", [](SenderFunc sender, int argc, char *argv[]) { - DynamicJsonDocument doc(1024); - JsonArray array = doc.to(); - scanWifi(array); - String str; - serializeJson(doc, str); - sender(str.c_str()); - }); - cmdHandler.registerCommand("connect", "Connect to wifi", [](SenderFunc sender, int argc, char *argv[]) { - if (argc <= 1) { - sender("INVAILD"); - return; - } - String ssid(argv[1]); - String password(argc > 2 ? argv[2] : ""); - if (connectWifi(ssid, password)) { - sender(WiFi.localIP().toString().c_str()); - WiFi.mode(WIFI_STA); - config.ssid = ssid; - config.password = password; - markDirty(); - } else { - sender("ERR"); - if (WiFi.getMode() == WIFI_STA && !connectWifi(config.ssid, config.password)) { - startHotspot(); - WiFi.mode(WIFI_AP); - } - } - }); - cmdHandler.registerCommand("disconnect", "Disconnect from wifi", [](SenderFunc sender, int argc, char *argv[]) { - startHotspot(); - sender("OK"); - WiFi.mode(WIFI_AP); - config.ssid = ""; - config.password = ""; - markDirty(); - }); - cmdHandler.registerCommand("name", "Get/set device name", [](SenderFunc sender, int argc, char *argv[]) { - if (argc <= 1) { - String str = config.name + String(',') + config.hostname; + cmdHandler.registerCommand("version", "Show version", + [](SenderFunc sender, int argc, char *argv[]) { + StaticJsonDocument<256> doc; + doc["product"] = product_name; + doc["model"] = model_name; + doc["id"] = ESP.getChipId(); + doc["version"] = version; + doc["versionCode"] = version_code; + doc["sdkVersion"] = ESP.getFullVersion(); + String str; + serializeJson(doc, str); + sender(str.c_str()); + }); + cmdHandler.registerCommand( + "status", "Show status", [](SenderFunc sender, int argc, char *argv[]) { + StaticJsonDocument<256> doc; + doc["vcc"] = ESP.getVcc() / 1000.0; + doc["resetReason"] = ESP.getResetReason(); + doc["freeHeap"] = ESP.getFreeHeap(); + doc["heapFragment"] = ESP.getHeapFragmentation(); + doc["maxFreeBlock"] = ESP.getMaxFreeBlockSize(); + doc["RSSI"] = WiFi.RSSI(); + FSInfo fs_info; + LittleFS.info(fs_info); + doc["fsTotalSpace"] = fs_info.totalBytes; + doc["fsUsedSpace"] = fs_info.usedBytes; + String str; + serializeJson(doc, str); sender(str.c_str()); - return; - } - config.name = argv[1]; - if (argc > 2) config.hostname = argv[2]; - markDirty(); - sender("OK"); - }); - cmdHandler.registerCommand("mode", "Get/set light mode", [](SenderFunc sender, int argc, char *argv[]) { - if (argc <= 1) { - String str = effect2str(lightEffect->type()); - sender(str.c_str()); - return; - } - EffectType type = str2effect(argv[1]); - if (type >= CONSTANT && type < EFFECT_TYPE_COUNT) { - delete lightEffect; - lightEffect = effectFactories[type](argc - 2, (const char **) argv + 2); - markDirty(); - sender("OK"); - } else { - sender("INVAILD"); - } - }); - cmdHandler.registerCommand("brightness", "Get/set brightness", [](SenderFunc sender, int argc, char *argv[]) { - if (argc <= 1) { - String str = String(config.brightness); - sender(str.c_str()); - return; - } - int brightness = atoi(argv[1]); - if (brightness >= 0 && brightness <= 255) { - if (config.brightness != brightness) { - FastLED.setBrightness(brightness); - FastLED.show(); - config.brightness = (uint8_t) brightness; - markDirty(); + }); + cmdHandler.registerCommand( + "config", "Get config", [](SenderFunc sender, int argc, char *argv[]) { + StaticJsonDocument<1024> doc; + if (WiFi.getMode() == WIFI_AP) { + struct ip_info info; + wifi_get_ip_info(SOFTAP_IF, &info); + doc["ip"] = IPAddress(info.ip.addr).toString(); + doc["mask"] = IPAddress(info.netmask.addr).toString(); + doc["gateway"] = IPAddress(info.gw.addr).toString(); + } else { + doc["ip"] = WiFi.localIP().toString(); + doc["mask"] = WiFi.subnetMask().toString(); + doc["gateway"] = WiFi.gatewayIP().toString(); } - sender("OK"); - } else { - sender("INVAILD"); - } - }); - cmdHandler.registerCommand("temperature", "Get/set temperature", [](SenderFunc sender, int argc, char *argv[]) { - if (argc <= 1) { - String str = String(config.temperature) + String('K'); + serializeSettings(doc); + String str; + serializeJson(doc, str); sender(str.c_str()); - return; - } - int temperature = atoi(argv[1]); - if (temperature >= 0) { - if (config.temperature != temperature) { - FastLED.setTemperature(CRGB(kelvin2rgb(temperature))); - FastLED.show(); - config.temperature = (uint32_t) temperature; + }); + cmdHandler.registerCommand("scan", "Scan wifi", + [](SenderFunc sender, int argc, char *argv[]) { + DynamicJsonDocument doc(1024); + JsonArray array = doc.to(); + scanWifi(array); + String str; + serializeJson(doc, str); + sender(str.c_str()); + }); + cmdHandler.registerCommand( + "connect", "Connect to wifi", + [](SenderFunc sender, int argc, char *argv[]) { + if (argc <= 1) { + sender("INVAILD"); + return; + } + String ssid(argv[1]); + String password(argc > 2 ? argv[2] : ""); + if (connectWifi(ssid, password)) { + sender(WiFi.localIP().toString().c_str()); + WiFi.mode(WIFI_STA); + config.ssid = ssid; + config.password = password; markDirty(); + } else { + sender("ERR"); + if (WiFi.getMode() == WIFI_STA && + !connectWifi(config.ssid, config.password)) { + startHotspot(); + WiFi.mode(WIFI_AP); + } } - sender("OK"); - } else { - sender("INVAILD"); - } - }); - cmdHandler.registerCommand("fps", "Get/set refresh rate", [](SenderFunc sender, int argc, char *argv[]) { - if (argc <= 1) { - String str = String(config.refreshRate); - sender(str.c_str()); - return; - } - int rate = atoi(argv[1]); - if (rate > 0 && rate <= 400) { - if (config.refreshRate != rate) { - if (timer.active()) timer.detach(); - timer.attach_ms(1000 / rate, updateLight); - config.refreshRate = (uint16_t) rate; + }); + cmdHandler.registerCommand("disconnect", "Disconnect from wifi", + [](SenderFunc sender, int argc, char *argv[]) { + startHotspot(); + sender("OK"); + WiFi.mode(WIFI_AP); + config.ssid = ""; + config.password = ""; + markDirty(); + }); + cmdHandler.registerCommand("name", "Get/set device name", + [](SenderFunc sender, int argc, char *argv[]) { + if (argc <= 1) { + String str = config.name + String(',') + + config.hostname; + sender(str.c_str()); + return; + } + config.name = argv[1]; + if (argc > 2) + config.hostname = argv[2]; + markDirty(); + sender("OK"); + }); + cmdHandler.registerCommand( + "mode", "Get/set light mode", + [](SenderFunc sender, int argc, char *argv[]) { + if (argc <= 1) { + String str = effect2str(lightEffect.type()); + sender(str.c_str()); + return; + } + EffectType type = str2effect(argv[1]); + if (type >= CONSTANT && type < EFFECT_TYPE_COUNT) { + lightEffect = + effectFactories[type](argc - 2, (const char **)argv + 2); markDirty(); + sender("OK"); + } else { + sender("INVAILD"); } - sender("OK"); - } else { - sender("INVAILD"); - } - }); + }); + cmdHandler.registerCommand("brightness", "Get/set brightness", + [](SenderFunc sender, int argc, char *argv[]) { + if (argc <= 1) { + String str = String(config.brightness); + sender(str.c_str()); + return; + } + int brightness = atoi(argv[1]); + if (brightness >= 0 && brightness <= 255) { + if (config.brightness != brightness) { + FastLED.setBrightness(brightness); + FastLED.show(); + config.brightness = + (uint8_t)brightness; + markDirty(); + } + sender("OK"); + } else { + sender("INVAILD"); + } + }); + cmdHandler.registerCommand( + "temperature", "Get/set temperature", + [](SenderFunc sender, int argc, char *argv[]) { + if (argc <= 1) { + String str = String(config.temperature) + String('K'); + sender(str.c_str()); + return; + } + int temperature = atoi(argv[1]); + if (temperature >= 0) { + if (config.temperature != temperature) { + FastLED.setTemperature(CRGB(kelvin2rgb(temperature))); + FastLED.show(); + config.temperature = (uint32_t)temperature; + markDirty(); + } + sender("OK"); + } else { + sender("INVAILD"); + } + }); + cmdHandler.registerCommand("fps", "Get/set refresh rate", + [](SenderFunc sender, int argc, char *argv[]) { + if (argc <= 1) { + String str = String(config.refreshRate); + sender(str.c_str()); + return; + } + int rate = atoi(argv[1]); + if (rate > 0 && rate <= 400) { + if (config.refreshRate != rate) { + if (timer.active()) + timer.detach(); + timer.attach_ms(1000 / rate, + updateLight); + config.refreshRate = (uint16_t)rate; + markDirty(); + } + sender("OK"); + } else { + sender("INVAILD"); + } + }); } ADC_MODE(ADC_VCC); // Enable ESP.getVcc() void setup() { - FastLED.addLeds(light.data(), light.count()); + FastLED.addLeds(light.data(), + light.count()); #ifdef LED_CORRECTION FastLED.setCorrection(CRGB(LED_CORRECTION)); #endif @@ -490,19 +517,19 @@ void setup() { webServer.send(302, MIME_TYPE(txt), ""); }); webServer.on("/version", HTTP_GET, []() { - cmdHandler.parseCommand([](const char *msg) { - webServer.send(200, MIME_TYPE(json), msg); - }, "version"); + cmdHandler.parseCommand( + [](const char *msg) { webServer.send(200, MIME_TYPE(json), msg); }, + "version"); }); webServer.on("/status", HTTP_GET, []() { - cmdHandler.parseCommand([](const char *msg) { - webServer.send(200, MIME_TYPE(json), msg); - }, "status"); + cmdHandler.parseCommand( + [](const char *msg) { webServer.send(200, MIME_TYPE(json), msg); }, + "status"); }); webServer.on("/config", HTTP_GET, []() { - cmdHandler.parseCommand([](const char *msg) { - webServer.send(200, MIME_TYPE(json), msg); - }, "config"); + cmdHandler.parseCommand( + [](const char *msg) { webServer.send(200, MIME_TYPE(json), msg); }, + "config"); }); static auto checkPath = [](const String &path) { if (path.isEmpty()) { @@ -621,11 +648,13 @@ void setup() { webServer.serveStatic("/", LittleFS, "/www/", "max-age=300"); webServer.begin(); Serial.println(F("Start WebSocket server")); - wsServer.onEvent([](uint8_t num, WStype_t type, uint8_t *payload, size_t length) { - switch (type) { + wsServer.onEvent( + [](uint8_t num, WStype_t type, uint8_t *payload, size_t length) { + switch (type) { case WStype_CONNECTED: { // payload is "/" IPAddress ip = wsServer.remoteIP(num); - Serial.printf_P(PSTR("Client %u connected from %s\n"), num, ip.toString().c_str()); + Serial.printf_P(PSTR("Client %u connected from %s\n"), num, + ip.toString().c_str()); break; } case WStype_DISCONNECTED: { @@ -633,23 +662,26 @@ void setup() { break; } case WStype_TEXT: { - char *str = (char*) payload; + char *str = (char *)payload; if (length > 0) { #ifdef ENABLE_DEBUG Serial.printf("Received message from ws%u: %s\n", num, str); #endif - handleCommand([num](const char *msg) { - wsServer.sendTXT(num, msg, strlen(msg)); - }, str); + handleCommand( + [num](const char *msg) { + wsServer.sendTXT(num, msg, strlen(msg)); + }, + str); yield(); } break; } - } - }); + } + }); wsServer.begin(); Serial.println(F("Start mDNS")); - if (MDNS.begin(config.hostname)) { // FIXME 电脑上的 chrome 无法主动发现设备, 但是 Android APP 能 + if (MDNS.begin(config.hostname)) { // FIXME 电脑上的 chrome + // 无法主动发现设备, 但是 Android APP 能 MDNS.addService("http", "tcp", 80); // MDNS.addService("ws", "tcp", 81); MDNS.addServiceTxt("http", "tcp", "product", product_name); @@ -660,7 +692,8 @@ void setup() { } void loop() { - if (config.isDirty && millis() - config.lastModifyTime >= CONFIG_SAVE_PERIOD) { + if (config.isDirty && + millis() - config.lastModifyTime >= CONFIG_SAVE_PERIOD) { saveSettings(); } while (Serial.available() > 0) { @@ -671,9 +704,7 @@ void loop() { #ifdef ENABLE_DEBUG Serial.printf("Received data from com: %s\n", buffer); #endif - handleCommand([](const char *msg) { - Serial.println(msg); - }, buffer); + handleCommand([](const char *msg) { Serial.println(msg); }, buffer); yield(); } } @@ -693,7 +724,8 @@ void printSystemInfo() { Serial.printf("SDK version: %s\r\n", ESP.getSdkVersion()); Serial.printf("CPU Freq: %dMHz\r\n", ESP.getCpuFreqMHz()); Serial.printf("Free heap: %u\r\n", ESP.getFreeHeap()); - Serial.printf("Heap fragment percent: %d%%\r\n", ESP.getHeapFragmentation()); + Serial.printf("Heap fragment percent: %d%%\r\n", + ESP.getHeapFragmentation()); Serial.printf("Max free block size: %d\r\n", ESP.getMaxFreeBlockSize()); Serial.printf("Sketch size: %u\r\n", ESP.getSketchSize()); Serial.printf("Sketch free space: %u\r\n", ESP.getFreeSketchSpace()); @@ -712,25 +744,36 @@ void printWifiInfo() { Serial.printf("Connected: %s\r\n", WiFi.isConnected() ? "true" : "false"); const char *wifi_status; switch (WiFi.status()) { - case WL_IDLE_STATUS: wifi_status = "Idle"; - case WL_NO_SSID_AVAIL: wifi_status = "No SSID"; - case WL_SCAN_COMPLETED: wifi_status = "Scan Done"; - case WL_CONNECTED: wifi_status = "Connected"; - case WL_CONNECT_FAILED: wifi_status = "Failed"; - case WL_CONNECTION_LOST: wifi_status = "Lost"; - case WL_DISCONNECTED: wifi_status = "Disconnected"; - default: wifi_status = "Other"; + case WL_IDLE_STATUS: + wifi_status = "Idle"; + case WL_NO_SSID_AVAIL: + wifi_status = "No SSID"; + case WL_SCAN_COMPLETED: + wifi_status = "Scan Done"; + case WL_CONNECTED: + wifi_status = "Connected"; + case WL_CONNECT_FAILED: + wifi_status = "Failed"; + case WL_CONNECTION_LOST: + wifi_status = "Lost"; + case WL_DISCONNECTED: + wifi_status = "Disconnected"; + default: + wifi_status = "Other"; } Serial.printf("Connection status: %s\r\n", wifi_status); - Serial.printf("Auto connect: %s\r\n", WiFi.getAutoConnect() ? "true" : "false"); + Serial.printf("Auto connect: %s\r\n", + WiFi.getAutoConnect() ? "true" : "false"); Serial.printf("MAC address: %s\r\n", WiFi.macAddress().c_str()); Serial.printf("Hostname: %s\r\n", WiFi.hostname().c_str()); Serial.printf("SSID: %s\r\n", WiFi.SSID().c_str()); Serial.printf("PSK: %s\r\n", WiFi.psk().c_str()); if (WiFi.status() == WL_CONNECTED) { Serial.printf("Local IP: %s\r\n", WiFi.localIP().toString().c_str()); - Serial.printf("Subnet mask: %s\r\n", WiFi.subnetMask().toString().c_str()); - Serial.printf("Gateway IP: %s\r\n", WiFi.gatewayIP().toString().c_str()); + Serial.printf("Subnet mask: %s\r\n", + WiFi.subnetMask().toString().c_str()); + Serial.printf("Gateway IP: %s\r\n", + WiFi.gatewayIP().toString().c_str()); Serial.printf("DNS 1: %s\r\n", WiFi.dnsIP(0).toString().c_str()); Serial.printf("DNS 2: %s\r\n", WiFi.dnsIP(1).toString().c_str()); Serial.printf("BSSID: %s\r\n", WiFi.BSSIDstr().c_str()); diff --git a/any.h b/any.h new file mode 100644 index 0000000..75101bb --- /dev/null +++ b/any.h @@ -0,0 +1,479 @@ +#pragma once +#ifndef _ANY_H +#define _ANY_H + +#if __cplusplus >= 201703L +#include +#else + +#define PARTICLE + +// +// Implementation of N4562 std::experimental::any (merged into C++17) for C++11 +// compilers. +// +// See also: +// + http://en.cppreference.com/w/cpp/any +// + http://en.cppreference.com/w/cpp/experimental/any +// + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4562.html#any +// + https://cplusplus.github.io/LWG/lwg-active.html#2509 +// +// +// Copyright (c) 2016 Denilson das Mercês Amorim +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include +#include +#include +#include +#include + +#if defined(PARTICLE) +#if !defined(__cpp_exceptions) && !defined(ANY_IMPL_NO_EXCEPTIONS) && \ + !defined(ANY_IMPL_EXCEPTIONS) +#define ANY_IMPL_NO_EXCEPTIONS +#endif +#else +// you can opt-out of exceptions by definining ANY_IMPL_NO_EXCEPTIONS, +// but you must ensure not to cast badly when passing an `any' object to +// any_cast(any) +#endif + +#if defined(PARTICLE) +#if !defined(__cpp_rtti) && !defined(ANY_IMPL_NO_RTTI) && \ + !defined(ANY_IMPL_RTTI) +#define ANY_IMPL_NO_RTTI +#endif +#else +// you can opt-out of RTTI by defining ANY_IMPL_NO_RTTI, +// in order to disable functions working with the typeid of a type +#endif + +namespace std { + + class bad_any_cast : public std::bad_cast { + public: + const char *what() const noexcept override { return "bad any cast"; } + }; + + class any final { + public: + /// Constructs an object of type any with an empty state. + any() : vtable(nullptr) {} + + /// Constructs an object of type any with an equivalent state as other. + any(const any &rhs) : vtable(rhs.vtable) { + if (!rhs.empty()) { + rhs.vtable->copy(rhs.storage, this->storage); + } + } + + /// Constructs an object of type any with a state equivalent to the + /// original state of other. rhs is left in a valid but otherwise + /// unspecified state. + any(any &&rhs) noexcept : vtable(rhs.vtable) { + if (!rhs.empty()) { + rhs.vtable->move(rhs.storage, this->storage); + rhs.vtable = nullptr; + } + } + + /// Same effect as this->clear(). + ~any() { this->clear(); } + + /// Constructs an object of type any that contains an object of type T + /// direct-initialized with std::forward(value). + /// + /// T shall satisfy the CopyConstructible requirements, otherwise the + /// program is ill-formed. This is because an `any` may be copy + /// constructed into another `any` at any time, so a copy should always + /// be allowed. + template ::type, any>::value>::type> + any(ValueType &&value) { + static_assert( + std::is_copy_constructible< + typename std::decay::type>::value, + "T shall satisfy the CopyConstructible requirements."); + this->construct(std::forward(value)); + } + + /// Has the same effect as any(rhs).swap(*this). No effects if an + /// exception is thrown. + any &operator=(const any &rhs) { + any(rhs).swap(*this); + return *this; + } + + /// Has the same effect as any(std::move(rhs)).swap(*this). + /// + /// The state of *this is equivalent to the original state of rhs and + /// rhs is left in a valid but otherwise unspecified state. + any &operator=(any &&rhs) noexcept { + any(std::move(rhs)).swap(*this); + return *this; + } + + /// Has the same effect as + /// any(std::forward(value)).swap(*this). No effect if a + /// exception is thrown. + /// + /// T shall satisfy the CopyConstructible requirements, otherwise the + /// program is ill-formed. This is because an `any` may be copy + /// constructed into another `any` at any time, so a copy should always + /// be allowed. + template ::type, any>::value>::type> + any &operator=(ValueType &&value) { + static_assert( + std::is_copy_constructible< + typename std::decay::type>::value, + "T shall satisfy the CopyConstructible requirements."); + any(std::forward(value)).swap(*this); + return *this; + } + + /// If not empty, destroys the contained object. + void clear() noexcept { + if (!empty()) { + this->vtable->destroy(storage); + this->vtable = nullptr; + } + } + + /// Returns true if *this has no contained object, otherwise false. + bool empty() const noexcept { return this->vtable == nullptr; } + +#ifndef ANY_IMPL_NO_RTTI + /// If *this has a contained object of type T, typeid(T); otherwise + /// typeid(void). + const std::type_info &type() const noexcept { + return empty() ? typeid(void) : this->vtable->type(); + } +#endif + + /// Exchange the states of *this and rhs. + void swap(any &rhs) noexcept { + if (this->vtable != rhs.vtable) { + any tmp(std::move(rhs)); + + // move from *this to rhs. + rhs.vtable = this->vtable; + if (this->vtable != nullptr) { + this->vtable->move(this->storage, rhs.storage); + // this->vtable = nullptr; -- unneeded, see below + } + + // move from tmp (previously rhs) to *this. + this->vtable = tmp.vtable; + if (tmp.vtable != nullptr) { + tmp.vtable->move(tmp.storage, this->storage); + tmp.vtable = nullptr; + } + } else // same types + { + if (this->vtable != nullptr) + this->vtable->swap(this->storage, rhs.storage); + } + } + + private: // Storage and Virtual Method Table + union storage_union { + using stack_storage_t = typename std::aligned_storage< + 2 * sizeof(void *), std::alignment_of::value>::type; + + void *dynamic; + stack_storage_t stack; // 2 words for e.g. shared_ptr + }; + + /// Base VTable specification. + struct vtable_type { + // Note: The caller is responssible for doing .vtable = nullptr + // after destructful operations such as destroy() and/or move(). + +#ifndef ANY_IMPL_NO_RTTI + /// The type of the object this vtable is for. + const std::type_info &(*type)() noexcept; +#endif + + /// Destroys the object in the union. + /// The state of the union after this call is unspecified, caller + /// must ensure not to use src anymore. + void (*destroy)(storage_union &) noexcept; + + /// Copies the **inner** content of the src union into the yet + /// unitialized dest union. As such, both inner objects will have + /// the same state, but on separate memory locations. + void (*copy)(const storage_union &src, storage_union &dest); + + /// Moves the storage from src to the yet unitialized dest union. + /// The state of src after this call is unspecified, caller must + /// ensure not to use src anymore. + void (*move)(storage_union &src, storage_union &dest) noexcept; + + /// Exchanges the storage between lhs and rhs. + void (*swap)(storage_union &lhs, storage_union &rhs) noexcept; + }; + + /// VTable for dynamically allocated storage. + template struct vtable_dynamic { +#ifndef ANY_IMPL_NO_RTTI + static const std::type_info &type() noexcept { return typeid(T); } +#endif + + static void destroy(storage_union &storage) noexcept { + // assert(reinterpret_cast(storage.dynamic)); + delete reinterpret_cast(storage.dynamic); + } + + static void copy(const storage_union &src, storage_union &dest) { + dest.dynamic = new T(*reinterpret_cast(src.dynamic)); + } + + static void move(storage_union &src, storage_union &dest) noexcept { + dest.dynamic = src.dynamic; + src.dynamic = nullptr; + } + + static void swap(storage_union &lhs, storage_union &rhs) noexcept { + // just exchage the storage pointers. + std::swap(lhs.dynamic, rhs.dynamic); + } + }; + + /// VTable for stack allocated storage. + template struct vtable_stack { +#ifndef ANY_IMPL_NO_RTTI + static const std::type_info &type() noexcept { return typeid(T); } +#endif + + static void destroy(storage_union &storage) noexcept { + reinterpret_cast(&storage.stack)->~T(); + } + + static void copy(const storage_union &src, storage_union &dest) { + new (&dest.stack) T(reinterpret_cast(src.stack)); + } + + static void move(storage_union &src, storage_union &dest) noexcept { + // one of the conditions for using vtable_stack is a nothrow + // move constructor, so this move constructor will never throw a + // exception. + new (&dest.stack) + T(std::move(reinterpret_cast(src.stack))); + destroy(src); + } + + static void swap(storage_union &lhs, storage_union &rhs) noexcept { + storage_union tmp_storage; + move(rhs, tmp_storage); + move(lhs, rhs); + move(tmp_storage, lhs); + } + }; + + /// Whether the type T must be dynamically allocated or can be stored on + /// the stack. + template + struct requires_allocation + : std::integral_constant< + bool, + !(std::is_nothrow_move_constructible::value // N4562 §6.3/3 + // [any.class] + && sizeof(T) <= sizeof(storage_union::stack) && + std::alignment_of::value <= + std::alignment_of< + storage_union::stack_storage_t>::value)> {}; + + /// Returns the pointer to the vtable of the type T. + template static vtable_type *vtable_for_type() { + using VTableType = + typename std::conditional::value, + vtable_dynamic, + vtable_stack>::type; + static vtable_type table = { +#ifndef ANY_IMPL_NO_RTTI + VTableType::type, +#endif + VTableType::destroy, VTableType::copy, + VTableType::move, VTableType::swap, + }; + return &table; + } + + protected: + template + friend const T *any_cast(const any *operand) noexcept; + template friend T *any_cast(any *operand) noexcept; + +#ifndef ANY_IMPL_NO_RTTI + /// Same effect as is_same(this->type(), t); + bool is_typed(const std::type_info &t) const { + return is_same(this->type(), t); + } +#endif + +#ifndef ANY_IMPL_NO_RTTI + /// Checks if two type infos are the same. + /// + /// If ANY_IMPL_FAST_TYPE_INFO_COMPARE is defined, checks only the + /// address of the type infos, otherwise does an actual comparision. + /// Checking addresses is only a valid approach when there's no + /// interaction with outside sources (other shared libraries and such). + static bool is_same(const std::type_info &a, const std::type_info &b) { +#ifdef ANY_IMPL_FAST_TYPE_INFO_COMPARE + return &a == &b; +#else + return a == b; +#endif + } +#endif + + /// Casts (with no type_info checks) the storage pointer as const T*. + template const T *cast() const noexcept { + return requires_allocation::type>::value + ? reinterpret_cast(storage.dynamic) + : reinterpret_cast(&storage.stack); + } + + /// Casts (with no type_info checks) the storage pointer as T*. + template T *cast() noexcept { + return requires_allocation::type>::value + ? reinterpret_cast(storage.dynamic) + : reinterpret_cast(&storage.stack); + } + + private: + storage_union storage; // on offset(0) so no padding for align + vtable_type *vtable; + + template + typename std::enable_if::value>::type + do_construct(ValueType &&value) { + storage.dynamic = new T(std::forward(value)); + } + + template + typename std::enable_if::value>::type + do_construct(ValueType &&value) { + new (&storage.stack) T(std::forward(value)); + } + + /// Chooses between stack and dynamic allocation for the type + /// decay_t, assigns the correct vtable, and constructs the + /// object on our storage. + template void construct(ValueType &&value) { + using T = typename std::decay::type; + + this->vtable = vtable_for_type(); + + do_construct(std::forward(value)); + } + }; + + namespace detail { + template + inline ValueType any_cast_move_if_true( + typename std::remove_reference::type *p, + std::true_type) { + return std::move(*p); + } + + template + inline ValueType any_cast_move_if_true( + typename std::remove_reference::type *p, + std::false_type) { + return *p; + } + } + + /// Performs + /// *any_cast>>(&operand), or + /// throws bad_any_cast on failure. + template + inline ValueType any_cast(const any &operand) { + auto p = any_cast::type>::type>(&operand); +#ifndef ANY_IMPL_NO_EXCEPTIONS + if (p == nullptr) + throw bad_any_cast(); +#endif + return *p; + } + + /// Performs *any_cast>(&operand), or throws + /// bad_any_cast on failure. + template inline ValueType any_cast(any &operand) { + auto p = + any_cast::type>(&operand); +#ifndef ANY_IMPL_NO_EXCEPTIONS + if (p == nullptr) + throw bad_any_cast(); +#endif + return *p; + } + + /// + /// If ValueType is MoveConstructible and isn't a lvalue reference, performs + /// std::move(*any_cast>(&operand)), otherwise + /// *any_cast>(&operand). Throws bad_any_cast + /// on failure. + /// + template inline ValueType any_cast(any &&operand) { + using can_move = std::integral_constant< + bool, std::is_move_constructible::value && + !std::is_lvalue_reference::value>; + + auto p = + any_cast::type>(&operand); +#ifndef ANY_IMPL_NO_EXCEPTIONS + if (p == nullptr) + throw bad_any_cast(); +#endif + return detail::any_cast_move_if_true(p, can_move()); + } + + /// If operand != nullptr && operand->type() == typeid(ValueType), a pointer + /// to the object contained by operand, otherwise nullptr. + template + inline const ValueType *any_cast(const any *operand) noexcept { + using T = typename std::decay::type; + +#ifndef ANY_IMPL_NO_RTTI + if (operand && operand->is_typed(typeid(T))) +#else + if (operand && operand->vtable == any::vtable_for_type()) +#endif + return operand->cast(); + else + return nullptr; + } + + /// If operand != nullptr && operand->type() == typeid(ValueType), a pointer + /// to the object contained by operand, otherwise nullptr. + template + inline ValueType *any_cast(any *operand) noexcept { + using T = typename std::decay::type; + +#ifndef ANY_IMPL_NO_RTTI + if (operand && operand->is_typed(typeid(T))) +#else + if (operand && operand->vtable == any::vtable_for_type()) +#endif + return operand->cast(); + else + return nullptr; + } + + inline void swap(any &lhs, any &rhs) noexcept { + lhs.swap(rhs); + } +} + +#endif /* __cplusplus >= 201703L */ + +#endif /* ANY_H */ \ No newline at end of file