Прежде чем кто-то скажет: «НЕ ДЕЛАЙТЕ ЭТОГО, так как это действительно плохо».
char mystr [] = {'m', 'y', '', 's', 't', 'r', 'i', 'n', 'g'};
Однако удобство представления c-string слишком велико.
Рациональным для этого является то, что я программирую для микроконтроллера, и мне нужно хранить данные в памяти программы. Некоторые данные представлены в форме байтов, слов, слов и плаваний. Я хотел бы, чтобы данные включали строки без NUL.
Я пробовал шаблоны, которые принимают <size_t N, символ * A> а также <size_t N, char (&А) [Н]> в качестве параметров, чтобы обойти массив и сохранить его содержимое в статическом массиве, но я не могу понять, что это правильно. Я думаю, что стандарт может фактически запретить это, что понятно в общем случае, но неудачно в конкретных случаях (в частности, в этом;;) :()
Если бы я мог переназначить строку как что-то вроде повышение :: MPL :: vector_c<символ, …> шаблон, это было бы лучше, так как у меня есть другой код, который будет правильно его хранить, но разыменование массива из шаблона, который будет использоваться в качестве параметра шаблона const, тоже не разрешено.
Есть идеи?
РЕДАКТИРОВАТЬ:
Пример псевдокода (это немного надумано, так как реальный код намного больше, также я бы, наверное, не читал побайтно, как этот, и не буду использовать литерал для итерации до конца строки. Это будет встроено в данные тоже где то.)
// this stores bytes in an array
template<typename X, typename T, T ...numbers>
struct x
{
static PROGMEM volatile const T data[];
};
template<typename X, typename T, T ...numbers>
PROGMEM volatile const T x<X, T, numbers...>::data[] = { numbers... };
void main()
{
// this will not work, but the idea is you have byte 0 as 1,
// byte 1 as 2 byte 2 as 3 byte 3 as 's', byte 4 as 'o'...
// byte 22 as 'g', byte 23 as 4, byte 24 as 5, byte 25 as 6.
typedef x<int, char, 1,2,3,"some embedded string",4,5,6> xx;
for(i=0; i<20; ++i)
Serial.print(pgm_read_byte_near(&xx::data[0] + 3));
}
Также обратите внимание, что я не использую C ++ 11, это C ++ 0x и, возможно, расширение.
магия и обман
Если вы использовали C ++ 11 (я знаю, но в его отсутствие я думаю, что генерация кода — ваш лучший выбор), кажется, что пользовательский литерал должен быть в состоянии справиться с этим. Например, с:
template <char... RAW>
inline constexpr std::array<char, sizeof...(RAW)> operator "" _fixed() {
return std::array<char, sizeof...(RAW)>{RAW...};
}
было бы неплохо, если бы это сработало:
const std::array<char, 7> goodbye = goodbye_fixed;
… но, к сожалению, это не так (литерал должен быть числовым, по-видимому, для разбора). С помощью "goodbye"_fixed
тоже не работает, так как это требует operator "" _fixed(const char *s, int length)
перегрузка и массив времени компиляции снова распался на указатель.
В конце концов мы дошли до вызова этого:
const auto goodbye = operator "" _FS <'g','o','o','d','b','y','e'>();
и это не лучше, чем уродливая первая версия. Есть другие идеи?
автоматически генерировать уродство
Я думаю, что вы правы, что вы не можете легко перехватить механизм строкового литерала.
Честно говоря, обычный подход состоит в том, чтобы использовать инструмент сборки для генерации уродливого кода для вас в отдельном файле (например, библиотеки интернационализации).
Например, вы печатаете
fixed_string hello = "hello";
или что-то подобное в выделенном файле, и система сборки генерирует заголовок
const std::array<char, 5> hello;
и CPP с уродливой инициализацией из выше ниже.
пропустил требование «выглядит как строковый литерал»
Я пробовал шаблоны …
как это?
#include <array>
const std::array<char, 5> hello = { 'h', 'e', 'l', 'l', 'o' };
#include <cstdio>
int main()
{
return std::printf("%.*s\n", hello.size(), &hello.front());
}
Если у вас нет C ++ 11, Boost.Array будет работать, или вы можете свернуть свой собственный.
Обратите внимание, что это просто оболочка типа const char[5]
, так что должно быть нормально, чтобы перейти в сегмент данных (я подтвердил, что идет в .rodata
с моим местным gcc).
Других решений пока нет …