Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > работа с template в C++11


Автор: Ivan. 10.8.2012, 12:13
Здравствуйте.
Я думаю уже многие оценили преимущества нового компилятора C++11, но для беспокойного программиста все таки остаются вопросы как реализовать ту или иную задачу.
У меня вопрос по template-ам. С появлением новых возможностей у меня снова появилось поле для размышления. Уже давно я пытаюсь создать строку с определенными атрибутами и получить на ее указатель непосредственно в месте ее использования.
Пример: 
Код
printf("Количество: %d", Count);

Здесь при вызове функции printf компилятор подставляет указатель на заранее подготовленную строку в коде или оперативной памяти.
Размещение строк в оперативной или кодовой памяти зависит от нескольких факторов:
- атрибут const;
- толерантность кодовой и оперативной памяти;
- единое или раздельное пространство областей памяти.
В моем случаи это микроконтроллер архитектуры AVR8, для которого памяти не толерантны и существуют 2 способа работы с переменными, расположенными в оперативной или кодовой памяти: printf и printf_P. Соответственно и создание переменных производится с разными атрибутами. Например:
Код
const char Text1[] = "Abc"; //В оперативной памяти
const char PROGMEM Text2[] = "Abc"; //В кодовой памяти

Получается, что для вызова функции со строкой, расположенной в кодовой памяти ее нужно заранее создать. Пример:
Код
const char PROGMEM Text[] = "Abc";
int main(){
    puts_P(Text);
}
Как вы понимаете - это очень неудобно, заранее подготавливать сотни строк, присваивать им какие то имена, при удалении мест использования пред подготовленных строк приходится их потом подчищать и т.д.

Вернемся к новому C++11, чем же он поможет нам с этой проблемой:
Шаблоны с переменным числом аргументов
Код
template<char ...Text> struct _ProgMemStr{static const char PROGMEM v[sizeof ...(Text)];};
template<char ...Text> const char _ProgMemStr<Text...>::v[] = {Text...};

Немного напомни новый синтаксис:
template<char ...Text> - шаблон с переменным числом аргументов типа char с присвоенным именем Text;
sizeof ...(Text) - возвращает количество аргументов;
С остальным все как раньше.
Что мы получаем?
Код
puts_P(_ProgMemStr<"Abc">::v);

Правда проблема осталась в том, что и новый C++ так и не научился принимать в качестве аргумента шаблона строку и придется писать следующим образом:
Код
puts_P(_ProgMemStr('A', 'b', 'c', '\0'); //Не удобно

Придумал следующую реализацию:
Код
#define ProgMemStr(T) _ProgMemStr<T[0], T[1], T[2], T[3], T[4], ..., T[N]>::v

Но она не работоспособна, так как компилятор ругается на то, что я пытаюсь взять символ строки выходящий за пределы массива. (была строка "Abc", а я пытаюсь взять 4, 5, и т.д. символ.
Идем дальше, прибавим к исходной строке еще N байт:
Код

#define _T "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
#define ProgMemStr(T) _ProgMemStr<(T _T)[0], (T _T)[1], (T _T)[2], (T _T)[3], (T _T)[4], ..., (T _T)[N]>::v

Следующая проблема - каждая строка будет занимать N байт. Эту проблему можно решить с помощью специальных шаблонов:
Код

template<size_t S, char ...Text> struct _ProgMemStr;
template<char T1, char ...> struct _ProgMemStr<1, T1, ...>{static const char PROGMEM v[S];};
template<char T1, char ...> const char _ProgMemStr<1, T1, ...>::v[] = {T1};
template<char T1, char T2, char ...> struct _ProgMemStr<2, T1, T2, ...>{static const char PROGMEM v[S];};
template<char T1, char T2, char ...> const char _ProgMemStr<2, T1, T2, ...>::v[] = {T1, T2};
//И так далее
#define _T "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
#define ProgMemStr(T) _ProgMemStr<sizeof(T), (T _T)[0], (T _T)[1], (T _T)[2], (T _T)[3], (T _T)[4], ..., (T _T)[N]>::v

Конечно код получается очень грамосским, но это вариант работает. Мне не нужно заботится о количестве символов, главное, чтобы не превысить N; Автоматически отсеиваются повторные темплейты и можно вызывать в любой момент:
Код

struct TMyStruct {const char* Name; int Value;};
TMyStruct MyValue = {ProgMemStr("Abc"), 10};
int main()
{
    puts_P(MyValue.Name);
    puts_P(ProgMemStr("Abc"));
}

Я возрадовался, когда решил эту многолетнюю задачу, компилятор с ней справляется довольно быстро и это то, что я хотел. С помощью темплейтов я научился создавать массивы объектов и массивы указателей на объекты, созданные с помощью таких же темплейтах и располагающие данные в кодовой памяти. Но счастье мое было не долгим. Оказывается, что в компиляторе существует предел на количество символов входных данных в теплейт - это 64к развернутых данных. конечно после сборки такой переменной она будет занимать всего пару килобайт, но ее не удается собрать. а каждая моя строка занимает 256 байт (этим я ограничился) независимо от желаемой строки "Abc", сотня таких строк съест 24к символов прекомпилятора как минимум (имеется ввиду если они будут посланы в один какой то очень сложный темплейт).

Вопрос, какой еще можно придумать способ создания строки не упираясь в данные ограничения компилятора?

Автор: korian 11.8.2012, 01:02
ндя, не понимаю, почему не придумали в компиляторе под avr что-то типа
P"My String", по типу как L"My string" для wchar_t

думаю надо порыть в сторону увеличения ограниченый компилятора, если у него есть такие параметры...

Автор: alexSl 11.8.2012, 01:37
Цитата(Ivan. @  10.8.2012,  12:13 Найти цитируемый пост)
Вопрос, какой еще можно придумать способ создания строки не упираясь в данные ограничения компилятора?


Может быть http://we.easyelectronics.ru/Soft/avr-s-i-umnye-ukazateli.html ?

Автор: borisbn 11.8.2012, 03:16
> В моем случаи это микроконтроллер архитектуры AVR8
> преимущества нового компилятора C++11
если не секрет, что за компилятор си++ 11 под микроконтроллер?

Автор: Ivan. 11.8.2012, 06:34
avrgcc-4.7.0-avrlibc-1.8.0 поддерживает c++11 гугли. есть скомпиленный для винды

Автор: rodnover 11.8.2012, 08:26
http://habrahabr.ru/post/140357/

Это не то? Переопределение собственных литералов. Правда сам этим еще так и не пользовался. Только читал.

Автор: Ivan. 11.8.2012, 15:17
литералы читал, строковые литералы выполняются в рантайме, а сырые литералы сводятся к темплейтам и возвращаемся к проблеме №1

Автор: korian 11.8.2012, 15:19
Все-таки, все уже "украдено" за нас smile

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=38003&start=all&postdays=0&postorder=asc
Цитата

The pgmspace.h header also exposes a neat little macro, PSTR, which by some GCC magic allows you to create inline strings:

Code:
LCD_puts(PSTR("Program Memory String"));


This stops you from having to clutter your program up with hundreds of variables which hold one-time-used strings. The downside to using the PSTR macro rather than a PROGMEM modified string variable is that you can only use the PSTR string once. 


Автор: Ivan. 11.8.2012, 15:25
Цитата(alexSl @  11.8.2012,  01:37 Найти цитируемый пост)
Может быть умные указатели ?

у меня проблема не в использовании, а в объявлении. в том примере он сперва создает строку с различными атрибутами, а уже потом присваивает к "Умному" указателю

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)