Pull to refresh

Comments 8

Для GCC вы можете делать как-то так:
#include <iostream>

#define PROGMEM

template <char... String>
struct ProgmemString {
    static const char PROGMEM v[sizeof...( String ) + 1];
};

template <char... String>
const char ProgmemString<String...>::v[sizeof...( String ) + 1] = {String..., '\0'};

template <typename CharT, CharT... String>
constexpr auto operator"" _progmem()
{
    return ProgmemString<String...>::v;
}

int main()
{
    std::cout << "progmem string"_progmem << std::endl;
}
С этим способом создания пользовательских литералов я не был знаком. Почитал вашу статью. Спасибо огромное.
Это, действительно, должно упростить раскрытие символов в макросе SPLIT_TO_CHAR.
Сейчас будем пробовать. Только компилятор обновим, а то наш еще не хочет такую строку кушать.
Гм. Может быть я не проник в суть статьи или отстал от жизни, но я все-таки не совсем понял, чем не устроил вариант

extern const char PROGMEM caption1[];
const char caption1[] = "Hello";
const char *pStr = caption1;


Собственно, я всегда примерно так и делал. Более того, это рекомендуемый в документации на avr-libc способ решения подобных проблем. Собственно все еще проще:

const char foo[] PROGMEM = "Hello, world!";

...

strcpy_P(dest,foo);


и, собственно, все.
Согласен, этот способ работает, но требуется написать две строки вместо одной.
strcpy_P(dest,SPS("Hello world!"));


Плюс сама строка располагается непосредственно в месте ее использования. Ваш пример сложно будет читать, если эти две строки не будут помещаться в экран.
Плюс не требуется придумывать имена для переменных, хранящих строку, что является лишним трудом.

Короче, лень — двигатель прогресса.

Кстати, рекомендуемый в документации способ, не работал одно время. Компилятор ругался, что __attribute__((__progmem__)) не может быть использован при создании переменной, а только в объявлении. Это было давно, но именно оттуда у меня привычка писать отдельно объявление, а потом инициализацию.
Понял.

Плюс сама строка располагается непосредственно в месте ее использования.


В сущности, разделение объявления строки и места ее использования можно считать фичей. Такой подход позволяет вынести сообщения в отдельный файл, и при необходимости локализации править только его. С именами особой проблемы тоже нет:

const char init_error_message[] PROGMEM = "Initialization failed.";
const char init_OK_message[] PROGMEM = "Hardware initialized.";

...


Но, конечно, тут на вкус и цвет.
Взял библиотеку уже готовую — например adafruit fona.
Там повсеместно используется конструкция

имя_функции(F("какой-то текст"));


«какой-то текст» располагается во флэш.
Реализацию не сложно посмотреть.

Хотя может и не понял глубины мысли в этом посте.
Тоже не понял глубину мысли. Все строки у меня PROGMEM или PSTR, т.е. в flash памяти кода. И все две функции работы со строками немного изменены и это «понимают». Ну нельзя просто так взять конкретный символ и что? pgm_read_byte( (char*)(&(str))+i ) — вот макрос для взятия символа. Ну разве что макросы — зло, а двухстраничные темплейты — добро.
Макрос F() — это аналог PSTR.
Весь смысл в том, что эти макросы не работают вне функций. А нам нужно было инициализировать глобальные переменные в кодовой памяти указателями на строку.

static const char *pStr = PSTR("Hello");
// error: statement-expressions are not allowed outside functions nor in template-argument lists


Естественно, мы делали так же, как указал LampTester, но нам было лень постоянно так делать и мы нашли такой способ.
Sign up to leave a comment.

Articles