почему не работает typedef enum?
- Войдите на сайт для отправки комментариев
Пт, 06/02/2015 - 15:23
в Си активно использую такие конструкции:
typedef enum { .... } my_enum; void my_function(my_enum v) { switch(v) { ..... } }
в этом случае компилятор на себя берет проверку содержания переменной типа my_enum. Это как бы на самом деле короткое или обычное целое, но компилятор не проверяет списочно. В Atmel Studio в Си проектах это работает, а вот в Ардуино он само описание typedef проглатывает, но при дальнейшей ссылке на тип говорит, что он не определен
Это известный глюк, мы уже где то на эту тему с кем то разбирались. Решение такое (создаем проект enum_test и сохраняем его):
1. В папаке проекта создаем .h файл, в нем описываем typedef. Файл enum_test.h. Лежит в той же папке, что и скетч:
2. Выгружаем АрудиноИДЕ и заходим снова, открываем проект. Видим две вкладки. enum_test.ino и enum_test.h.
2. Файл enum_test.ino:
Компилируется. Но я не запускал, на работе не на чем.
PS Писал подробно, мало ли кому пригодится.
UPS: Кстати, что бы не было вопросов. Делаем как обычно, всё в одном файле:
Компилируем. Выводятся ошибки. Смотрим файл, который компилирует АрдуиноИДЕ и видим "прикол":
Здесь четко видно, что описание функции "void func(Enum en);" появляется ДО объявления типа "typedef enum _Enum".
Да вот все не так просто
у меня собственно была рабочая программа на Си и мне понадобилось подключить библиотеку ардуиновскую. Чтобы каждый раз не переписывать библиотеку на Си решил один раз переписать свою программу.
Определение enum у меня и так в другом файле, но это не спасает. Здесь похоже тупость сборщика ардуино в том, что он исходники собирает в один файл, но при этом вначале файлы сортирует по алфавиту. Сейчас вспомнил, что уже с этим сталкивался. Значит дело не только в вынесении в файл, но еще и в порядке следования имен файлов по алфавиту
Главное в скетче ошибка, а в библиотеке (файл .c) ошибки нет
Можете выслать мне проект, например, на (здесь был адрес), я посмотрю завтра. Мне и самому будет полезно узнать :)
UPD: адрес удалил
это проект из статьи (там есть Си код) http://radiokot.ru/circuit/digital/home/194
я его только начал на ардуину переводить
Более простой вариант: если в коде есть include каких-либо файлов, то если объявить enum до include, то ошибки не будет.
UPS: Кстати, что бы не было вопросов. Делаем как обычно, всё в одном файле:
Компилируем. Выводятся ошибки. Смотрим файл, который компилирует АрдуиноИДЕ и видим "прикол":
К стати в 1.6.7 этот код компилится, работает или нет на работе не могу проверить, не на чем :-(
UPS: Кстати, что бы не было вопросов. Делаем как обычно, всё в одном файле:
Компилируем. Выводятся ошибки. Смотрим файл, который компилирует АрдуиноИДЕ и видим "прикол":
К стати в 1.6.7 этот код компилится, работает или нет на работе не могу проверить, не на чем :-(
Да, на 1.6.7. компилируется, поскольку там многое уже поменялось. А почему он не должен работать?
А почему так нельзя?
Подскажите, а возможно в
вместо hello изменяймое значение например String?
Есть задача - нужно парсить шину и при совпадении присваивать переменную.
например запрос такого вида:
&var4=1
разбиваем его на строки и получаем var4 и значение 1
затем нужно присвоить переменной с названием var4 значение 1 (что бы в последующем в программе удобно было её использовать)
Вот была мысля сделать через enum
Скажите, возможно сравнивать названия переменных из enum?
Например, http://www.quizful.net/post/enum-types-c
Спасибо,
Может есть более "простые" способы решить мою задачу?
Чего то я совсем в тупик пришёл(
Хотя какой то мутант у меня таки получился, как более менее причешу - выложу, если конечно расчёску не сломаю))
Есть предположение, что это может быть связано с тем что Arduino написано на C++. И разработчики делают упор на классы и наследование. При этом не пытались реализовать возможности структур и объединений. Изначально была заложена концепция конструктора. Берешь среду разработки, собираешь из кубиков текст скетча и готово. Для гибкого програмирования есть серьезные среды разработки Студия, IAR, Keil. По сути должно работать, вы наверное что то делаете не правильно. Ведь в принципе typedef enum это просто перечисление констант или переменных. Что можно описать как обычное присвоение с указанием типа переменной.
А можно это описать и так
Спасибо,
Может есть более "простые" способы решить мою задачу?
Чего то я совсем в тупик пришёл(
Зачем в твоей задаче нужен enum? Этот вопрос задай себе и ответь себе честно. Лично я не вижу нужды в enumе, у тебя есть имя переменной и её значение, т.е., втупую, простейшая структура, условно: struct variable { name; value; }, создать объект, который содержит список этих variable. Добавить в него методы addVariable(name, value), getVariable(name) и т.п. Я в своё время делал такое, но на большом компе и оно сильно заточено на специальные задачи. Здесь бы хорошо подошел map, но на микроконтроллере я эти штуки не использовал, думаю жрут память немеренно.
Вообще задача слишком абстрактная, чтобы давать какие либо советы.
Вот, что у меня получилось на основе примера. Это решение работает в точности как мне надо, но есть подозрение, что это заход не стой стороны)) К сожалению мои познания в с++ - больше результат практического использования, поэтому в более сложных задачах начинаются танцы с бубном)
valuename.h
Сдесь задаём названия переменных (их будет много)
enum_helper_pre.h
enum_helper_post.h
Сам код
Ок, чтобы не разбрасываться временем, насколько я понял, переменных всегда определенное количество? Это навеяно строкой 072.
Если это так, и даже если это не так, то количество переменных - ограничено, т.е. заранее известно (ну или известно, что их не более M, у тебя это число 10, поскольку переменных не может быть больше 10), тогда проще сделать их размещение в массиве со значениями (values[]) позиционно-зависимым. В этом случае мы делаем константный массив имен переменных (можно во флеше, PROGRMEM. Собственно он у тебя уже есть), индекс переменной в этом массиве - дает нам позицию, где хранится эта переменная. И наоборот, по индексу можно считать имя переменной и её значение. Всё, что надо, пишется в одном методе getTokenIndex, который по имени найдет индекс переменной в values. Ну если уж так нужны enumы, тогда getTokenEnum вернет enum значение, чтобы потом им можно было пользоваться в switch, например. К тому же, enum может быть и индексом, поскольку всегда можно написать так: enum { var6 = 0, var7, var4, var12, maxVars }; здесь var6 = 1, var7 = 1, var4 = 2, var12 = 3, maxVars = 4 (максимальное количество переменных, тогда можно объявлять int values[maxVars];).
Строки 091 и 092 не соответствуют друг другу. Строка 072 намертво блокирует отображение переменных. Просто закомментарить, пусть всегда выводит текущее значение переменных.
PS Возможно это не новость, но strtok содержит статик переменную, потому одновременно (вперемежку) нельзя две разных строки парсить этой функцией. Хотя здесь всё нормально, сначала одна, потом другая. И да, она портит исходную строку, которую надо парсить. Если знаешь, забей, может кому то будет полезно.
Да, колличество (их может быть больше 100) и имена переменных известны зарание - мы ведь должны знать, что парсим...
Массив переменных у меня есть, его я и использую values[]. По соответствию его индекса и индекса
valuename_strs[] я и назначаю ему переменную value.
Но как туда записать и считать имя переменной, я чего то совсем не понимаю. И как я смогу потом сравнивать имя с парсингом через switch?
Не могли бы сделать маленький пример из всего того, что написали выше.
Спасибо
Да, колличество (их может быть больше 100) и имена переменных известны зарание - мы ведь должны знать, что парсим...
Массив переменных у меня есть, его я и использую values[]. По соответствию его индекса и индекса
valuename_strs[] я и назначаю ему переменную value.
Но как туда записать и считать имя переменной, я чего то совсем не понимаю. И как я смогу потом сравнивать имя с парсингом через switch?
Зачем имя записывать в массив? Мы всегда значем, что переменная с именем var6 хранится в values[0], с именем var7 хранится в values[1], с именем var4 в values[2], var12 в values[3].
Если нужно имя переменной, берите её из массива с именами переменных по индексу, например, имя переменной var6 берите из valuename_strs[0] и так далее.
Итак:
значение var7 храним в values[var7]
значение var6 храним в values[var6]
и т.д.
Здесь я считаю, что var6, var7... объявлены согласно моему примеру enum VARNAME { var6 = 0, var7, var4, var12, maxVars }; т.е. они имеют последовательные номера от 0 и до максимального.
По-поводу switch с enum:
На счет имени переменной. Она нужна только на момент определения индекса/enum значения. В дальнейшем имя нам не нужно ни с чем сравнивать, поскольку у нас есть индекс/enum значение для этой переменной. По нему мы можем достать значение Serial.println(values[varName]); или её имя Serial.println(valuename_strs[varName]);
Чего то я совсем запутался.. В последнем посте изложено одно решение или несколько вариантов?
имена переменных храним в:
Как мне поместить имена в массив, без предварительного декларирования ?
Без предварительного декларирования - никак.
Я понял, ваш вариант я рассматривал в самом начале, просто была задача, что бы названия переменных указывались в одном месте. - скетч универсальный и часть большого проекта. Названия перменных каждый будет менять под свои нужды и будет очеь не удобно, если это надо будет в разных местах + если их будет например 100 - то верный путь к ошибкам..
Скажите, а чем плох мой первоначальный пример? Какие у него минусы? он по идеи должен использовать пинимум памяти мк.
И возможно ли декларацию переменных вынести из файла valuename.h в тело скетча?
Скажите, а чем плох мой первоначальный пример? Какие у него минусы? он по идеи должен использовать пинимум памяти мк.
Насколько я понимаю, это оно и есть, позиционно-зависимое расположение данных. На счет небольших минусов, но это опять же моё, я не люблю эти String, к тому же там String Valuename глобальная переменная и char *Valuename локальная переменная. Аккуратней с этим. Глобальные переменные можно использовать, если они нужны в разных частях кода программы, а если они используются только в одной функции, то нет смысла выносить их в глобальную часть. Если не перевру, то есть такая рекомендация "объявлять переменные нужно как можно ближе к тому месту, где они используются", разумеется я не собираюсь здесь обсуждать в куче эти переменные будут или в стеке или еще где, вопрос не в этом. А остальное вроде ничего особо в глаза не бросается, а в подробностях ковыряться как то некогда.
Я такие макросы не люблю, понятно, это моя проблема, не тема для обсуждения. А вцелом, объявление и нужно выносить в тело если не скетча, то в cpp файл, а не оставлять в заголовочном. Заголовочные файлы нужны (в данном контексте) для описания внешних переменных (extern int Value), но не для объявления самих переменных. Есть проблемы?
Переделал вот так
Я такие макросы не люблю, понятно, это моя проблема, не тема для обсуждения. А вцелом, объявление и нужно выносить в тело если не скетча, то в cpp файл, а не оставлять в заголовочном. Заголовочные файлы нужны (в данном контексте) для описания внешних переменных (extern int Value), но не для объявления самих переменных. Есть проблемы?
Проблема в том, что среда ардуина выносит файлы .Н в вкладки, а их с кодом программы 25 штук. - не хотелось бы ещё больше запутывать людей.
5-6 строки создают String, который можно и обойти, для формирования int есть старая добрая функция strtod (либо, чуть проще atoi, но без контроля) http://www.cplusplus.com/reference/cstdlib/strtod/ Типа:
На счет того, что среда выводит все файлы проекта во вкладки - это уж как есть и это совсем не значит, что нужно из-за этого всё класть в один файл. Либо "терпеть", либо использовать другую среду, но это, думаю, и так понятно. Другой вопрос, правильно ли то, что в проекте 25 файлов, возможно это не оптимально, а может и нет, это тебе видней. Еще, как вариант, части реализовать в виде библиотек, но это совсем другая история.
Большое спасибо за помошь!