Динамическая инициализация глобальной константы
- Войдите на сайт для отправки комментариев
Доброго дня, уважаемые!
На днях захотелось мне странного. Вопрос вызывает у меня чисто-академический интерес (пока).
Есть класс, описывающий поведение определенного оборудования - модульного резервуара для жидкости. Должен быть создан один глобальный объект класса. Одно из основных свойств - максимальная вместимость, константа из констант. Соответственно, так она в классе и описана. Но существует крайне-редкий случай, когда значение этого свойства должно быть изменено пользователем - увеличение/уменьшение количества присоединенных модулей.
Вопрос, думаю, теперь понятен: как можно поменять предустановленное значение константы без правки скетча и перепрошивки модуля (ESP32)?
Вижу такую последовательность мероприятий: при изменении параметров оборудования пользователь сообщает контроллеру новое значение емкости. Контроллер каким-то образом это значение фиксирует, выполняет рестарт с подхватом этого нового значения.
Собственно, с самой динамической инициализацией проблем никаких нет. На старте конструктор класса выполняет инициализацию константы расчетным путем при помощи другого метода класса. Но в этой ситуации новое значение уже должно быть доступно классу. А вот где его хранить пре рестарте контроллера я не понимаю. EEPROM не прокатывает, поскольку i2c еще не инициализируется на этот момент. Чтение EEPROM возвращает 0хFF. Если объект класса инициализировать локально (в сетапе), все работает как дОлжно, но этот вариант для меня не вариант.
Скетч
#include "I2c_FM_EEPROM.h" i2cEEPROM i2c(0x57); uint16_t v1{0}; // debug const uint16_t maxCapacity{2000}; class Tank{ public: Tank(); uint16_t getCap() {return _maxLevel;} private: const uint16_t _maxLevel; uint16_t getMaxCap(); }; Tank::Tank(): _maxLevel{getMaxCap()}{} uint16_t Tank::getMaxCap(){ uint16_t def{maxCapacity}; // Дефолтное значение uint16_t mem{i2c.read16(100)}; // Попытка чтения альтернативного значения из i2c EEPROM v1 = mem; // Отладка: результат чтения if(mem%1000 == 0) return mem; // Корректное значение должно быть кратно 1000 (размер одного модуля) return def; } Tank tank; void setup() { // Tank tank; // c локальным объектом все работает корректно Serial.begin(115200); Serial.print("v1: "); Serial.println(v1); // Что было прочитано из EEPROM - возвращает 65535 Serial.print("i2c.read16(100): "); Serial.println(i2c.read16(100)); // Что на самом деле в EEPROM - возвращает 6000 Serial.print("tank.getCap(): "); Serial.println(tank.getCap()); // Как отработал конструктор - возвращает 2000 i2c.write16(100, 6000); // Запись пользовательского значения в EEPROM } void loop(){}
Возврат, дословно:
65535
6000
2000
Инициализация Wire в библиотеке EEPROM:
#pragma once #include <Wire.h> // Превышение максимального размера буфера чтения/записи I2C_BUFFER_LENGTH не отслеживается! class i2cEEPROM { public: i2cEEPROM(uint8_t addr) {_i2cAddr = addr; Wire.begin(); _selfTest();} uint16_t write(uint16_t addr, void* buf, size_t len); uint8_t write8(uint16_t addr, uint8_t buf); uint16_t write16(uint16_t addr, uint16_t buf); uint16_t write32(uint16_t addr, uint32_t buf); uint16_t read(uint16_t addr, void* buf, size_t len); uint8_t read8(uint16_t addr); uint16_t read16(uint16_t addr); uint32_t read32(uint16_t addr); void printDump(); uint16_t maxAddress() {return _maxaddr;} void erase(uint8_t tmpl = 0); private: void _selfTest(); uint8_t _i2cAddr; uint16_t _maxaddr = 65535; };
Вижу 2 боковых варианта: при обновлении оборудования шить новую прошивку, забить на константность. Но очень не хочется идти на компромисс.
Идеи, наставления и оскорбления приветствуются))
Убери const и не придумывай себе сложностей на ровном месте.
Описать глобальный указатель, сделать на него new после инита eeprom?
А зачем const?
Убери const и не придумывай себе сложностей на ровном месте.
В "боевой" версии проекта так и есть.
Описать глобальный указатель, сделать на него new после инита eeprom?
А зачем const?
Идею с указателем протестирую, спасибо. Видится const, потому что по физическому смыслу это константа при данном наборе оборудования
.
Благородный дон, Вы вместо того, чтобы толком объяснить задачу, пытаетесь объяснять уже придуманное решение.
Первый вопрос - сколько вариантов ёмкости вообще возможно? Сколько вариантов "нацеплять модули"? Наверняка ведь 2-3, ну 4 варианта и не больше. Так, на практике. Нет? Ну пусть даже 8. И что, ради этих несчастных 3 вариантов Вы собрались писать фичу ввода новой ёмкости и извращаться? Не проще ли заранее выписать в документации все варианты, например так:
На два свободных пина завести вот такой переключатель (или два обычных джампера) и на нём набирать двоичный код варианта. Поменялось что-то - щелкнул тумблерами (поставил как надо джамперы) и нажал на ресет. Ну, а конструктор класса тупо читает пины и выставляет выбранную ёмкость. Для 4 вариантов требуется сдвоенный переключатель, для восьми - строенный (как на картинке) - они разные продаются.
Ну, не знаю. Мне тоже кажется, что const надуман тут. Кто переменную изменит извне?
Можно с сеттерами/геттерами извращаться, но тоже не особо вижу смысла.
Да, дело не в const, а в том, что ради этого предполагается ещё ввод значения от юзера писать. По мне так это совершенно лишнее.
Если объект класса инициализировать локально (в сетапе), все работает как дОлжно, но этот вариант для меня не вариант.
А, кстати, почему?
С учетом особенностей языка Си++, а также специфики микроконтроллеров вообще и Ардуино в частности - вполне стандартное решение. Более того, нередко вообще единственно возможное.
sadman41, для меня вопрос из области саморазвития. Безусловно, я уже перерос тот уровень, когда мог накосячить с произвольным изменением переменных. Идея с new кажется перспективной. Даже если не получится (хотя не вижу, почему не должно), подтяну практику в этой области.
ЕвгенийП, Евгений Петрович, поскольку оно не работает, это не решение :). Просто хотел показать, что уже пытался предпринять.
Общая емкость хранения от 2 кубометров до 6 с шагом 1 куб. Сейчас 2, к концу мая будет разовое увеличение до 4. К концу лета возможно еще увеличение на 1-2 единицы То-есть число вариантов очень ограничено, 3-х битного переключателя должно хватить. Как альтернатива EEPROM - вполне. Но при таком раскладе склонюсь к использования неконстантной переменной
С учетом особенностей языка Си++, а также специфики микроконтроллеров вообще и Ардуино в частности - вполне стандартное решение. Более того, нередко вообще единственно возможное.
Нужна глобальная область видимости и статическое время жизни. С этим классом будут плотно взаимодействовать другие классы из разных частей программы: планировщики, исполнительные устройства, интерфейс внешних коммуникаций... Не очень удобно будет с локальным работать
Спасибо откликнувшимся! На текущий момент с большим отрывом лидирует идея не заморачиваться константами. Если не взлетит new, так и будет.
А идея создавать объектов по максимуму, а пользоваться только теми что инициализировались не появлялась ?
Не понял, как это? Отдельно придется всем "смежникам" объяснять с каким экземпляром нужно работать. Уж больно мудрЕно
Нужна глобальная область видимости и статическое время жизни.
И каким образом это противоречит стандартному для Ардуино способу инициализации? (когда аппаратнозависимая инициализация статической переменной происходит не в конструкторе, а в методе begin/init, вызываемом в setup)
И каким образом это противоречит стандартному для Ардуино способу инициализации? (когда аппаратнозависимая инициализация статической переменной происходит не в конструкторе, а в методе begin/init, вызываемом в setup)
Это не статическая, а константа. Я не знал, что их можно инициализировать не в конструкторе.. Это же присвоение получается, а не инициализация? Или я чего недогоняю?
ЕП может поведать что там в стандарте, но мне представляется, что реализация отдана на откуп компилятора. Он же иногда просто константу по коду раскидает, нигде ее отдельно не храня.
В данном случае я все ещё с трудом понимаю необходимость const. Каков профит от этой конструкции?
Пример с "отложенной" ициализацией: https://github.com/adafruit/Adafruit_NeoPixel/blob/master/Adafruit_NeoPi... стр.131
Если я правильно понял логику происходящего, это сишная вариация на тему #2
sadman41, спасибо за идею с указателем. Сейчас добрался до макетки - это работает.
Возврат: