Как оптимально разделять код (методология создание слождного проекта)?

osetroff
Offline
Зарегистрирован: 27.08.2014

Я не спец в Си.

Прекрасно понимаю, как все части проекта побить на классы, для каждого из которых сделать <класс>.h и <класс>.cpp (например eeprom.h и eeprom.cpp, nrf.h и nrf.cpp).

Но, сильно смущает скорость исполнения кода, в котором все функции вызываются через указатели на классы.

Допустим, отказываюсь от классов. Какова альтернатива?

Могу переменные, относящиеся ранее к классу, поместить в struct <класс> {..} и эта структура описывается  в <класс>.h, а ее экземпляр объявляется в скетче.

Но вот с функциями то как быть?

Могу их разнести по .cpp файлам.Однако при #include <..>.cpp arduino IDE их не находит.

Может кто посоветует как разбить сложный проект на части без использования классов?

Или пошлите меня куда .. по ссылке.

Благодарю

 

std
Offline
Зарегистрирован: 05.01.2012

Тож интересует. Либа для часов 1307 жжёт напалмом, со своими запросами к оперативе.

osetroff
Offline
Зарегистрирован: 27.08.2014

Еще, например, в <...>.h

byte rr;

void int();

В <...>.cpp 

void int() {rr++;};

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

Чего-то я туплю.

osetroff
Offline
Зарегистрирован: 27.08.2014

Может ее переименовать и удалить все ненужные процедуры и тп?

Michal
Michal аватар
Offline
Зарегистрирован: 26.04.2013

ругается потому, что название функции совпадает с названием типа int

osetroff
Offline
Зарегистрирован: 27.08.2014

Нет, это я пример неправильно описал. Пусть rr_int.

Ругается так: 



In function `rr_int()':
Z:\libraries\k_retrans/k_retrans.cpp:5: multiple definition of `rr'
kretrans010.cpp.o:/arduino/kretrans010.ino:22: first defined here

И так на каждую переменную, объявленную в .h и используемую в rr_int.

То есть, насколько я понимаю, компилятор считает, что переменная rr в функции и переменная rr в .h  - не одно и то же.

Как его заставить поверить, не используя классы?

Передавать переменные в функцию тоже не могу. Их бывает много. Накладно.

osetroff
Offline
Зарегистрирован: 27.08.2014

Меня бы устроил совершенно тупой метод склеивания скетча из тучи разных файлов.

Такое чудо есть на Си?

С .h и .cpp файлами уже два часа бьюсь - безрезультатно.

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

Т.е. хочется, изменив один файл, получить изменения сразу для всех частей проекта при сборке.

 

osetroff
Offline
Зарегистрирован: 27.08.2014

Ура, получилось:

Просто делаю 



#include "путь\eeprom.cpp"
#include "путь\nrf.cpp"

И, таким образом, получаю скетч, склеенный из множества cpp файлов, каждый из которых вмещает глобальные переменные и функции для отдельной части проекта.

Всем спасибо.

Последнее, никак не получается путь вынести в #define, чтобы при смене пути не заменять везде путь.

#define globalpath z:\k
#include "globalpath\eeprom.h"

не работает

 

Datak
Offline
Зарегистрирован: 09.10.2014

osetroff, я бы не стал сильно беспокоиться насчёт снижения скорости из-за использования классов. В "серьёзных" процессорах скорость почти не снижается, а в некоторых случаях даже увеличивается, при правильно написанном коде.

Ардуиновские AVR-контроллеры, может быть, не так хорошо заточены под классы, но поверьте, в ардуине есть много мест, которые снижают эффективность намного больше.

По поводу ошибки "multiple definition of ..." - надо просто один раз чётко понять, для чего принято использовать файлы  .c  или  .cpp, и для чего  .h
После этого всё станет легко и просто. :)

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

Вы в своём коде именно создали функцию  rr_int  в  .h-файле.

Сравните: 

















int rr_int( );

это объявление. Компилятору сказали, что функция  rr_int  существует, что она возвращает значение  int, и что параметров у неё нет. Такое объявление можно распихивать в любом месте Вашего кода, сколько угодно раз. Ошибки это не вызовет, и размер скомпилённого файла не увеличит.

















int rr_int( )
{
   return 0;
}

а это уже описание. Компилятор должен перевести эти строчки на "машинный язык", и запихнуть их в генерируемый файл. Если Вы разместили такое описание в .h-файле, а потом включили этот файл в несколько .cpp-файлов, компилятор (а точнее линкер) попытается поместить эту функцию в выходной файл несколько раз. Этого делать бессмысленно, и поэтому запрещено - вот Вам и выдаётся соответствующая ошибка.

Datak
Offline
Зарегистрирован: 09.10.2014

osetroff пишет:

Ура, получилось:

Просто делаю 







#include "путь\eeprom.cpp"
#include "путь\nrf.cpp"

И, таким образом, получаю скетч, склеенный из множества cpp файлов, каждый из которых вмещает глобальные переменные и функции для отдельной части проекта.

Оужос! Для чего я всё это писал? :)

Ладно, можно и так конечно. Но это неправильно. То есть правильно, но криво - а значит не правильно.

osetroff
Offline
Зарегистрирован: 27.08.2014

Благодарю. Ясно.

А как из #define путь в #include прописать , не подскажете?

P.S.Почему не правильно, если работает, как нужно?

Мои цели:

1)отказаться от классов;

2)структурировать код;

3)сделать подобие библиотек;

достигнуты.

Это же для микроконтроллера пишется, приходится экономить.

Вот у меня, к примеру 490us подается питание на катушку соленоидного клапана, 1000us-490us не подается.

Скорость важна.

Или, например, для радиосети мне проще буфер сделать фиксированной длины 32x20=640байт, а оперативки-то тоже не так много.

 

Gippopotam
Gippopotam аватар
Offline
Зарегистрирован: 12.09.2014

std пишет:

Тож интересует. Либа для часов 1307 жжёт напалмом, со своими запросами к оперативе.

Используйте либу для 3231

Datak
Offline
Зарегистрирован: 09.10.2014

osetroff пишет:

А как из #define путь в #include прописать , не подскажете?

Тысячи извинений - но не подскажу. Обычно, дополнительные пути поиска для  #include  задаются не в самих файлах, а в опциях компилятора. Но как к этим опциям добраться  в ардуине не знаю, может ещё кто-то подскажет. Вообще, гугл говорит, что опции можно менять в ардуиновском файле "Documents and Settings\<user_name>\Application Data\Arduino\preferences.txt".

Для gcc-компилятора вроде должна помочь опция -Iнаш_global_path. Я попробовал, но ничего не получилось.

Опять же, буду благодарен если кто-то подскажет. Иногда поменять какие-то ключи компилятора очень даже хочется.

Цитата:

P.S.Почему не правильно, если работает, как нужно?

Ну, может и погорячился немного, или не понял что-то. Если ваша цель - удобно разместить файлы по каталогам, и использовать некоторые в разных ардуинопроектах - то да, действительно так быстро ничего придумать не получается.

Но если все файлы в одном каталоге, как у этой ардуины принято - смысла включать .cpp-файлы один в другой не вижу. Они же и так все "склеиваются".

А экономить, и место и время - тут я согласен, я тоже всегда за это. Только в ардуиносреде у меня это плохо получается. Теряюсь я в ней, как-то тут всё бесконтрольно - что сама хочет, то и творит. Я так не привык. :)

Datak
Offline
Зарегистрирован: 09.10.2014

osetroff пишет:

сильно смущает скорость исполнения кода, в котором все функции вызываются через указатели на классы

И уж ещё, в защиту классов. :)

Функции не вызываются через указатели. Во всяком случае, пока вы не используете виртуальные функции.

Через указатели передаются "классовые" данные для этих функций. Но я бы не сказал, что скорость обращения к этим данным ниже, например, по сравнению с глобальными. Несколько тактов, правда, тратится на установку самих этих указателей - но думаю не много.

А ещё, можно создавать классы, в которых все данные объявлены как static. В этом случае, класс действительно служит только для структуризации. Генерируемый код при этом ничем не будет отличаться от обычного, "бесклассового".

osetroff
Offline
Зарегистрирован: 27.08.2014

О! Вот про статические классы спасибо - обязательно попробую!

Удачи!

P.S.

А чем в командной строке можно из hex файла получить типа листинг asm?

Хочется таки разобраться, как вызываются функции и грузятся переменные при ссылке на статический класс.

Чтобы не беспокоиться о неоптимальной скорости работы с классами в циклах.

Datak
Offline
Зарегистрирован: 09.10.2014

osetroff пишет:

А чем в командной строке можно из hex файла получить типа листинг asm?

Один из способов - использовать avr-objdump.exe. Он может из .elf-файла вытащить очень много интересной и полезной информации. Правда, я им не очень владею - мало пользовался. Но ассемблер, вместе с полученным машинным кодом, можно сгенерировать командой

c:\Arduino\hardware\tools\avr\bin\avr-objdump.exe -d имя_проекта.cpp.elf

Каким-то ключом можно наверно и выходной файл задать -  скорее всего -oимя_файла или -Oимя_файла. Но точно не скажу. Я сам просто перенаправлял вывод в командной строке:

c:\Arduino\hardware\tools\avr\bin\avr-objdump.exe -d имя_проекта.cpp.elf >имя_файла.txt

Сам .elf-файл создаётся ардуиносредой где-то во временном каталоге, причём, насколько я понял, каталог при каждом запуске создаётся новый. Не слишком удобно, но можно привыкнуть.

 

Другой способ - заставить сам компилятор генерить подробный листинг. Но для этого, опять же, надо знать как передать ему определённые ключи, а я такой фокус в ардуине пока не освоил. Помогите кто-нибудь! :)

Datak
Offline
Зарегистрирован: 09.10.2014

Datak пишет:

гугл говорит, что опции можно менять в ардуиновском файле  preferences.txt.

Для gcc-компилятора вроде должна помочь опция -Iнаш_global_path. Я попробовал, но ничего не получилось.

Сумел всё-таки. Только не в  preferences.txt,  а в  platform.txt  получилось.

И ещё пришлось версию IDE обновить - в старой такого файла не было.