Как вынести функции в отдельный файл?

Тема в разделе "Микроконтроллеры AVR", создана пользователем Faberge, 4 июн 2015.

  1. Faberge

    Faberge Administrator Администратор

    Добрый день.

    Мне нужно вынести функции из программы в отдельный файл (их слишком много и код постоянно приходится пролистывать). Проблема в том, что мне не нужны классы и прочие навороты (как описано в мануалах на сайте ардуино, тем более, у меня не получилось создать свою библиотеку), нужно просто скрыть их с глаз, но чтобы они работали. Как это можно сделать? Хоть бы даже простой txt файлик подключить, они ведь без форматирования.

    Спасибо.
     
  2. geher

    geher Гуру

    Простой способ, работающий, но совершенно ни разу не правильный (не соответствует догматам C/C++):
    Выделяем часть кода в отдельный файл, укладываем рядом со скетчем, а потом в том месте скетча, где оно должно находиться, вставляем конструкцию #include "имя нашего файла с выделенными кусками кода".
    Препроцессор еще до компиляции врежет содержимое дополнительного файла в скетч в том самом месте, где находится директива #include

    Вариант второй, более правильный:
    Выделяем часть кода, содержащую требуемые функции в отдельный файл. При этом данные функции не должны использовать никаких функций, переменных и классов из основного файла программы. Требование необязательное, но его несоблюдение потребует построения дополнительных связей в виде объявления переменных из другого модуля как extern, описания прототипов функций и создает излишнюю сложность связей кода и серьезно осложняет повторное использование кода. Короче говоря, лучше без крайней необходимости так не делать.
    файлу даем какое-нибудь имя и расширение .cpp. Например, file.cpp.
    Делаем второй файл с прототипами функций, которые будут использоваться в основном файле программы. Даем ему то же имя, но другое расширение - .h
    Выкладываем получившиеся два файла в папку со скетчем.
    добавляем в начало скетча строку
    #include "имя получившегося файла с расширением.h"

    Сверхпростой и в общем-то бессмысленный пример.
    Допустим, что в наш файл была выделена функция function, имеющая параметром int и возвращающая int.

    Файл с функцией file.cpp:
    Код (Text):

    int function(int i)
    {
        return(i);
    }
     
    Файл с прототипом функции file.h:
    Код (Text):

    int function(int);
     
    Скетч (test.ino):
    Код (Text):

    #include "file.h"
    setup()
    {
       Serial.begin(9600);
    }
    loop()
    {
       Serial.println(function(millis()%2));
    }
     
    Вариант третий, еще более правильный.
    То, что получилось во втором варианте копируется в папку с библиотеками в подпапку с именем, которое совпадает с именем файла с функциями без расширения. А еще лучше, оформляется в архив, как библиотека, чтобы было просто ее устанавливать в IDE.

    PS. Arduino IDE не содержит вменяемых средств для работы с проектами, содержащими более одного файла.
    Она умеет по факту только открывать уже готовый такой проект, показывая все его файлы (они должны все лежать в одной папке) во вкладках, и подключать к проекту внешние библиотеки.
    Так что придется делать все это расчленение во внешнем текстовом редакторе, укладывая результаты в папку со скетчем, а потом уже открывать его в Arduino IDE.
    Если же делается библиотека (вариант три) то файлы библиотеки придется всегда править чем-то внешним по отношению к Arduino IDE.
     
    Последнее редактирование: 5 июн 2015
    Alex19, Faberge, M0ln1a и ещё 1-му нравится это.
  3. Tomasina

    Tomasina Сушитель лампочек Модератор

    если нужно "нужно просто скрыть их с глаз, но чтобы они работали", то используй вкладки в IDE (тыц).
    Следующий этап - использовать функции, как расписано выше.
    Ну и для профи - классы и библиотеки.
     
    geher нравится это.
  4. geher

    geher Гуру

    За ссылку спасибо. О некоторых сочетаниях клавиш не знал.
     
  5. Unixon

    Unixon Оракул Модератор

    Забыли еще про "guard headers" рассказать.

    Содержимое mylibrary.h должно быть оформлено со следующими директивами
    Код (Text):
    #ifndef MYLIBRARY_H
    #define MYLIBRARY_H

    // прототипы функций

    #endif
    Если этого не сделать, повторное включение заголовочного файла через #include вызовет ошибки.
     
  6. Megakoteyka

    Megakoteyka Оракул Модератор

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

    Tomasina Сушитель лампочек Модератор

    я испытываю к ним отношение как к чему-то большому и страшному :)
    Подробная статья по их написанию и применению была бы очень уместна.
     
  8. Unixon

    Unixon Оракул Модератор

    К библиотекам или классам?
     
  9. Tomasina

    Tomasina Сушитель лампочек Модератор

    для меня пока одно от другого мало чем отличается.
    Вот тут все толково расписано, но пинка для ускорения самообразования не хватает.
     
  10. Unixon

    Unixon Оракул Модератор

    Совершенно разные вещи. Объекты и классы - элементы языка, библиотека - форма организации проекта.
     
  11. Unixon

    Unixon Оракул Модератор

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

    Ну вот, смотрите. Был у вас такой файл main.c
    Код (Text):

    #include <iostream>

    int foo(int a, int b)
    {
    return a+b;
    }

    int main()
    {
    int a,b;
    std::cin>>a>>b;
    std::cout<<foo(a,b);
    return 0;
    }
     
    И тут вы решили, что все, кроме main(), здесь лишнее и сделали так:
    foo.h
    Код (Text):

    #ifdef FOO_H
    #define FOO_H

    int foo(int a, int b);

    #endif
     
    foo.c
    Код (Text):

    #include "foo.h"

    int foo(int a, int b)
    {
    return a+b;
    }
     
    main.c
    Код (Text):

    #include <iostream>
    #include "foo.h"

    int main()
    {
    int a,b;
    std::cin>>a>>b;
    std::cout<<foo(a,b);
    return 0;
    }
     
    Поздравляю, вы создали исходный код приложения с библиотекой "foo".
     
    Последнее редактирование: 8 июн 2015
    Faberge нравится это.
  12. Unixon

    Unixon Оракул Модератор

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

    Библиотеки бывают статические (файлы *.a, *.lib, и т.п.) - они также включаются в проект во время компиляции (а еще точнее, компоновки), но не в виде исходного кода, а в виде готового заранее скомпилированного кода, и динамические (*.so, *.dll и т.п.) - они подключаются во время исполнения программы. На микроконтроллерах гарвардской архитектуры динамические библиотеки смысла не имеют, к использованию пригодны только статические в виде скомпилированных бинарников или исходного кода.
     
    Последнее редактирование: 11 июн 2015
  13. Unixon

    Unixon Оракул Модератор

    Вот как происходит компиляция стандартного примера Blink:

    Из файла скетча Blink.ino создается Blink.cpp путем добавления в начало файла строки
    #line 1 "Blink.ino"
    Потом код скетча компилируется. Из исходного файла Blink.cpp получается объектный файл Blink.cpp.o
    Поскольку библиотека Wiring подключается не в собранном виде, а в виде отдельных исходных файлов, сначала каждый из них компилируется в объектный файл *.o :
     
  14. Unixon

    Unixon Оракул Модератор

    Затем, объектные файлы библиотеки Wiring собираются в статическую библиотеку core.a
    После чего производится компоновка (линковка) исполняемого файла Blink.cpp.elf
    И, наконец, генерируется hex-файл прошивки.
     
  15. Faberge

    Faberge Administrator Администратор

    Припозднился с ответом, но благодарю за такой подробный комментарий. :)
    С библиотеками осовился и на радостях создал сразу три, чтобы часто путаться :)
    Единственное, у меня еще вопрос: как я понял, при директиве #include <something.h> препроцессор просто подгружает в исходный файл кусок текста из файла "something.h". Но если в этом файле уже есть дополнительно директива #include <defines.h> и происходит новое включение, то почему мне не удалось собрать всё, до тех пор, пока я не включил в главный файл эту же строку <defines.h>.
    Выглядит так, что файл приходится подключать к проекту два раза, несколько странно.
     
  16. Максим B

    Максим B Гуру

    Теперь всю эту ветку оформить в wiki ))