Работа с шаблонами
- Войдите на сайт для отправки комментариев
Здравствуйте, начла заниматься шаблонами, решил это все засунуть в бибиотеку.
В чем суть программы - создать одну строку которая будет выглядить примерно так: &имя_переменной=значение_переменной
Мы создаем структуру который в себе хранит имя и значение, работаем со значением, когда нам нужно собрать строку для отправки мы вызываем функцию класса processingData, которая примет нашу структуру, определит тип значения которое в структуре, и вызовет нужную перегрузку и допишит к имени нужный символ.
Я создал шаблонную структуру, и пытался передать ее в качестве аргумента в функцию класса
вот код .h файла
#ifndef myLibrarie_h #pragma once #if defined(ARDUINO) && ARDUINO >= 100 #include "Arduino.h" #else #include "WProgram.h" #endif template<typename T>struct dataStruct { String name; T value; }; class myLibrarie { public: String data = "&"; myLibrarie(); void packetReceive(); void processingData(dataStruct<T> &variable); String toString(String stringValue); private: void definitionType( int value, String &name); void definitionType( float value, String &name); void definitionType( bool value, String &name); void definitionType( char value, String &name); }; #endif
вот код .cpp файла
#include "myLibrarie.h" void myLibrarie::myLibrarie() { Serial.begin(9600); } void myLibrarie::definitionType( int value, String &name) { name += "I"; } void myLibrarie::definitionType( float value, String &name) { name += "F"; } void myLibrarie::definitionType( bool value, String &name) { name += "B"; } void myLibrarie::definitionType( char value, String &name) { name += "C"; } String myLibrarie::toString(String stringValue) { stringValue = String(data, DEC); return stringValue; } void myLibrarie::processingData(dataStruct<T> &variable) { data += definitionType(variable.value, variable.name); }
Пытался скомпилировать выдало ошибку
myLibrarie.h:25:34: error: 'T' was not declared in this scope
И
myLibrarie.h:25:35: error: template argument 1 is invalid
После покомпался в интернете и сделал такой код
вот код .h файла
#ifndef myLibrarie_h #pragma once #if defined(ARDUINO) && ARDUINO >= 100 #include "Arduino.h" #else #include "WProgram.h" #endif template<typename T> struct dataStruct { String name; T value; }; template<typename T>class myLibrarie { public: String data = "&"; myLibrarie(); void processingData(dataStruct<T> &variable); String toString(String stringValue); private: void definitionType( int value, String &name); void definitionType( float value, String &name); void definitionType( bool value, String &name); void definitionType( char value, String &name); }; #endif // #ifndef myLibrarie_h
вот код .cpp файла
#include "myLibrarie.h" template<typename T>void myLibrarie<T>::myLibrarie() { Serial.begin(9600); } template<typename T>void myLibrarie<T>::definitionType( int value, String &name) { name += "I"; } template<typename T>void myLibrarie<T>::definitionType( float value, String &name) { name += "F"; } template<typename T>void myLibrarie<T>::definitionType( bool value, String &name) { name += "B"; } template<typename T>void myLibrarie<T>::definitionType( char value, String &name) { name += "C"; } template<typename T>String myLibrarie<T>::toString(String stringValue) { stringValue = String(data, DEC); return stringValue; } template<typename T>void myLibrarie<T>::processingData(dataStruct<T> &variable) { data += definitionType(variable.value, variable.name); }
Это код который я использую в arduino IDE (к первому варианту кода и ко втрому)
#include <myLibrarie.h> void setup() { // put your setup code here, to run once: } void loop() { String objName = "Hello"; dataStruct<int> firstObj; myLibrarie<int> Obj; firstObj.name = objName; firstObj.value = 5; Obj.processingData(firstObj); }
И этот код возвращает ошибку:
Что у Вас делают определения типа template<typename T> в .cpp файле? Откуда компилятору знать какие именно T Вы подсунете? Это же самостоятельный файл. Он компилируется отдельно от всего остального.
Это делается либо прямо в .h файле, либо в CPP но тогда с конкретными типами.
Это делается либо прямо в .h файле, либо в CPP но тогда с конкретными типами.
Я убирал
из .cpp файла, но мне нужно использовать структуру которая объявлена в .h как:
Теперь вопрос как подсунуть ему T из .h файла?
Пишите всё в .h файле. Без .cpp вообще. Шаблонные библиотеки так пишутся чуть реже, чем всегда.
У меня к примеру есть функция которая принимает шаблонную структру
Вопрос класстоже должен быть шаблонный и иметь свой тип, я перекинул все в .h файл, если не использовать фунцкию то все ок, но как только я использую функцию мне выдает ошибку
У меня к примеру есть функция которая принимает шаблонную структру
Вопрос класстоже должен быть шаблонный и иметь свой тип?
Не могу сказать, чтобы я понял вопрос, но ... тот факт, что где-то используется шаблон не накладвает никаких требований на использующие его конструкции. Никаких. Если Вам надо её использовать - используйте и не парьтесь. Например, если Вы пользуетесь EEPROM, Вы наверняка пользовались функциями EEPROM.put и EEPROM.get. А Вы знаете как они написаны?
И ничего. Народ пользует, даже не догадываясь, что там шаблоны.
Шаблон же это просто подсказка компилятору как создать функцию для нужного типа и не более того. Не особой разницы написать несколько функций с одним именем для разных типов и одну шаблонную.
Вы бы сделали маленький пример (совсем маленький) и отладилиь бы не нём
Я написал шаблонные функции которые работают вне какого-либо класса, и все ок. Они принимают любые типы данных и все прекрасно работает, но как только я пытаюсь сделать тоже с классом выдает ту ошибку.
Есть пример в интернете только для Visual Studio
Но на арудино он не работает.
P.S cout я удалял, в функции loop я только создал объект класса, и пытался вызвать функцию
если у тебя нет диплома доктора философии, шаблоны классов на Ардуне ты никада не освоишь.
Зато потом те, кто с такими дипломами, да шаблоны освоили, за 50 тыщ даже с дивана не встають.
Думай.
Просто смысл в том что на десктопе они работают, а на Ардуине нет
Так на ардуине Си нечестный. Он даже знатоков двадцати языков облапошивает как детей малых.
Научитесь сначало правильно создавать классы , и только потом лезте к шаблонам классов.
И тоже самое через шаблоны
P.S cout я удалял, в функции loop я только создал объект класса, и пытался вызвать функцию
Что Вы там удаляли? Что создавали? А главное, как?
Если Вы хотите подсказки по неработающему коду, так и публикуйте его, а не тот, который "на десктопе работает".
С http://cppstudio.com/post/5188/ я взял просто 100% работающий пример, чтобы проверит будет ли он работать у меня
Понятно. Значит Вашего кода, который Вы запускали, мы так и не увидим.
Этот код Ардуино я использовал для своего кода
Это код к коду с cppstudio
Этот код Ардуино я использовал для своего кода
Да, нет, не этот. Тут по меньшей мере не хватает описания dataStruct и myLibrarie. Может они в отдельном файле, но тогда всё равно не хватает include. Даже если Вы его где-то 100500 постов назад и выкладывали - это ничего не меняет. Никто не будет искать что-то и думать то ли Вы его с тех пор меняли, то ли нет.
Это код к коду с cppstudio
Вот на это глубоко наплевать. Больше не постите.
ChaNger,
послушайте, если Вам действительно нужна помощь (пока ещё хоть кому-то не надоело пытаться Вам помочь), Вы пожалуйста, не умничайте, не отбрасывайте то, что по-Вашему правильно, не публикуйте ХЗ каких кодов с ХЗ каких сайтов. Просто возмите Ваш код полностью, так чтобы его можно было просто сохранить и запустить. Выложите его и объясните толком в чём у Вас проблема.
Я Ваc понял вот это полный код .h файла
Вот это код Ардуино
И компилятор выдает ошибку
Ну, эта ошибка уйдёт сразу, как только Вы правильно объявите переменную Obj, а именно, либо уберёте скобки в строке №3 (просто уберите нафиг), либо уж напишете их правильно. лучше просто убрать.
Этой ошибки больше не будет, но появится новая. Дело в том. что у Вас в классе все методы private, а Вы пытаетесь обращаться к ним снаружи. Вставьте в строку 20 текст "public:" (без кавычек, конечно).
После этого всё начнёт нормально компилироваться, но это не значит, что ошибок больше нет. Но, сначала скомпилируйте и посмотрите, что получается.
Обратите внимание, ни одна из двух ошибок, о которых я говорю никак не связана с шаблонами. Это касается абсолютно любых классов.
Да работает, спасибо.
Но у меня остался вопрос:
Я создаю объект класса и структуры
Они оба имеют тип Int
Но когда я пытаюсь передать в функицю структуру с типом данных float выдает ошибку и впринципе логично
Но как мне сделать чтобы функция могла принимать любые структуры, я так делал с функциями и все работало (функция была вне какого-либо класса и структуры, и могла принимать любые типы структур)
У меня получилось
Но как теперь правильно саму функцию вне класса тоесть вот это:
Вы сделали свой класс myLibrarie шаблонным, т.е. явно зависящим от типа, так что не жалуйтесь.
Если Вам надо, чтобы сам по себе класс myLibrarie от типа не зависел, а только одна функция была шаблонной, так Вы так и пишите. Класс - без шаблона, а одна (или несколько) функций с шаблонами. Кто Вам мешает-то?
Пример как делать нешаблонный класс с шаблонными методами, можете посмотреть в библиотеке EEPROM. Там класс EEPROMClass нешаблонный, а его методы put и get - шаблонные. Вот также и делайте.
Раз сегодня пятница, позвольте и мне набросить...
Значится, самоучебная задача такая: для массива {typedef enum, char*} сочинить два шаблона функций поиска - того и другого. Т.е. искать строку, соответствующую enum и enum, соотвествующий строке.
С первым (enum -> string) я более-менее справился (хотя и читал в интернетах, что надёжнее передавать в функцию размер массива самостоятельно, а не доверять компилятору). Но вот второе поставило меня в тупик. Сначала столкнулся с передачей строк в шаблон. Грязно захачил. Затем на моём пути встал возврат результата пользовательского типа... Тут уже без пинка не получается. Пнёте?
templates_test.ino
templates.h
Передавать длину через параметр шаблона - крайне плохая идея. Ну, совсем плохая. Видимо, там где Вы читали совет так не делать, автор не потрудился объяснить почему, иначе бы Вы так делать не стали.
Уверен, что Вы сами поймёте почему так делать не стоит, я только "пинок" дам. Но если вопросы останутся, спрашивайте.
Итак, "пинок".
Скомпилируйте Вашу программу как есть. А потом, удалите строку №52 и, соответственно, конечную запятую в строке 51 и скомпилируйте снова. Сравните объём полувшегося кода в первом и втором случае. Именно кода, Бог с ними - с данными. Если вопросы останутся, то поэкспериментируйте с различными размерами массивов.
Да, я уже без компилирования понимаю, что на выходе появится стотыщмильёнов сгенерированных функций, каждая под свою размерность массива (массивов). Позаимствованная под вечер книга Х.М.Дейтела рассказала, что template это развитие идеи клонирования через #define, а медитация и мерное раскачивание вместе с согражданами в очередном сеансе любви к родине через пользование общественным транспортом, расставило всё на свои места. Надеюсь. Вобщем-то изначально я действовал через указатели и внешний arraySize вычислитель, но потом захотелось красоты. Так что советчик из интернетов не виноват.
Таким образом, как я понимаю, правильный шаблон функции enum => char* будет выглядеть так:
А вызов этак:
Но, вот как заведести во вторую char* и вывести enum, не являющийся типом аргумента, я так и не понял пока. В книжке лихо оперируют возвращаемыми типами, которые так же применяются и для аргументов, таким образом "проявляясь" в итоговой функции. А тут надо как-то намекнуть компилятору, что следует возвратить. Я пытался, но он говорил, что у него с дедукцией проблема и он ничего делать не будет поэтому. Да и со строковыми параметрами, как я понял, у механизма шаблонизации какая-то идеологическая несовместимость...
Есть ли выход (а вернее - вход)?
Неудобно, а шо делать. Ну или так если кого-то очень сильно корежит.
Не знаю, зачем Вам возможность использовать в качестве ключа и то, и другое, но это можно сделать красвио и по эфективности не хуже, чем Вы пытаетесь.
Но для шаблонов нужно, чтобы все допустимые типы поддерживали одинаковые операции. Поэтому первно, что нужно сделать - это класс-обёртку для char *. Класс ни о чём - ему надо иметь конструкторы от char * и const char *, преобразование к char * и обязательно операции сравнения > < и т.п.
Далее, в Вашу структуру нужно обавить акцессоры - функции, который возвращают элемент структуры.
Тогда всё станет однородным и Вы сможете не только затемплатить поиск по люому полю, но и даже праделить класс"массив стуктур" и у него операцию взятия индекса.
Не знаю, зачем Вам возможность использовать в качестве ключа и то, и другое, но это можно сделать красвио и по эфективности не хуже, чем Вы пытаетесь.
Прямо вот так, чтобы всегда в обе стороны мапить - не всегда надо, но строку в enum хотелось бы. Например при разборе URL имя ресурса строковое получаем, но хранить строку в памяти - тупо. Еnum удобней. При генерации ответной страницы неплохо с той же структуры дёрнуть что-то связанное с текущим URI, таскаемым по рантайму enum-ом. Или хранить соответствие "состояние сервиса - описание - html class ". И в PROGMEM уложить этот "справочник" Такая задумка, вобщем.
Ну вот например простой шаблон массива.
Объявляем енам, класс и массивы для трех типов.
Должна быть прстроена реализация только для типов
DayOfWeek String и int
.Вот вывод поиска.
Микроконтроллеры, которые плохо вели себя в прошлой жизни, в этой получают прошивку на C++.
Сдаётся мне, что дешевле будет возвращать из шаблона индекс массива, соответствующий результату поиска, а в основном коде по нему уже дёргать соотв. поле структуры.
Не знаю, зачем Вам возможность использовать в качестве ключа и то, и другое, но это можно сделать красвио и по эфективности не хуже, чем Вы пытаетесь.
Прямо вот так, чтобы всегда в обе стороны мапить - не всегда надо, но строку в enum хотелось бы. Например при разборе URL имя ресурса строковое получаем, но хранить строку в памяти - тупо. Еnum удобней. При генерации ответной страницы неплохо с той же структуры дёрнуть что-то связанное с текущим URI, таскаемым по рантайму enum-ом. Или хранить соответствие "состояние сервиса - описание - html class ". И в PROGMEM уложить этот "справочник" Такая задумка, вобщем.
пиши ужо, как есть: "колхозю жсон парсер, хачу, как у людей - вжжжих! - и строка распарсена в словари, и доступ по ключу к любому полю". ;)))))
=======================
Я не лезу, как можно заметить, почти никогда в подобные ветки - про создание "универсального чуда" для МК.
Тут есть основная причина: есть две противоречищие стратегии в программировании: удобство кодера и эффективность кода. В МК - мало ресурсов и удобство кодера, чисто ИМХО, никого не интересует. Если позволят ресурсы, то, вАААААще нет никакого спора - не только можно, но и нужно писать на NodeJS или MicroPython, которые УЖЕ реализованы для подходящих по ресурсам МК. На любом из упомянутых языков, то же жсон парсер выглядит РОВНО ОДНОЙ командой. Потому, что типонезависимый словарь/map/ассоциативный массив (это почти синонимы) - входит в базовые типы языка (JS - так просто на нем основан). То есть в этих языках УЖЕ есть то, что ты пишешь. В С++ для большой машины это есть в STL, не так удобно, как в JS или Python, но есть.
Не уверен, что для AVR, с ее ресурсами это нужно и полезно, а для ARM - "всё уже украдено до нас!" ;))) Как-то так.
Прости, я не хочу исполнять "танец птицы обломинго". Просто ты же мужик умный и не обидишься. ;)) Если ты чисто ради "скилл апгрейда" это делаешь, как баловство - то и вопросов нет.
Нет, жсон парсер не пишу. Делаю ровно то, что озвучил: описание статуса сервиса/тцп сокета выкидываю в html-поток, подсвечивая ошибочные состояния красным, не очень опасные - оранжевым, хорошие - зелёным. В принципе, трансформацию делаю, нагородив под этот тип функции "enum => description", "enum => htmlClass". Базу данных на МК не устраиваю, держу в прогмеме несколько массивов по 5-7 записей из 2-3 полей.
Так, ну вот... Работая по методе DetSemen-а, накропал простецкое (в классы с итераторами не рискнул соваться):
templates_test.ino
templates.h
Вроде теперь не должно быть излишнего размножения шаблонных функций?
Очень прошу! Если тебя так прет от идей ООП, почитай про "выведение типов". При правильном применении темплейтов, параметры в уловых скобках не нужно указывать. В твоем случае именно так -- оба параметра компилятор сам высичлит из типа слева и типа параметра.
Попробуй! Должно понравится! ;))))
Дак я не от хорошей жизни до конкретизации шаблона докатился. Компилятор отказывался дедуктировать и прикидывался ветошью.
И от идей меня не прёт вообще, на данном этапе развития так проще достигается цель.
Смотри: если убрать тип только из возвращаемого и внести под скобки, то выведение работает. По мне - так достаточно красиво.
я, конечно, проверил.
--------------------------------
Каюсь - по возвращаемым - не умеем. я с чем-то перепутал, вероятно, может с жавой? ;))
и темплейты:
Да, проверил себя, в джаве тоже так нельзя.
Ну тогда можно, как я много где видел, сделать return не void а продублировать ID. Но аргумент ID с нужным типом под скобки включить придется, по мне - так это красивее, чем в угловых скобках что-то писать, но тут - "на вкус и цвет...." и что-то там про фломастеры.
Дальнейшее развитие идеи и её практическое применение показало, что удобней сделать так:
С вызовом
Две птички одним камнем: возвращаемый тип через формальный параметр заводим, избавляясь от конкретизации шаблона, и обрабатываем ситуацию с именем, в соответствие которому не поставлен ни один Id.