Классы и объекты: сколько и какие типы файлов мне действительно нужно использовать?

У меня нет предыдущего опыта работы с C ++ или C, но я знаю, как программировать C # и изучать Arduino. Я просто хочу организовать свои эскизы и вполне устраиваю язык Arduino даже с его ограничениями, но я действительно хотел бы иметь объектно-ориентированный подход к программированию Arduino.

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

  1. Один файл .ino;
  2. Несколько файлов .ino в той же папке (что IDE вызывает и отображает как «вкладки»);
  3. Файл .ino с включенными файлами .h и .cpp в той же папке;
  4. То же, что и выше, но файлы - это установленная библиотека внутри папки программы Arduino.

Я также слышал о следующих способах, но пока не получил их:

  • Объявление класса C ++ в одном и том же одиночном файле .ino (слышал, но никогда не видел, что это возможно?);
  • [предпочтительный подход] . Включает файл .cpp, где объявлен класс, но без с использованием .h-файла (хотелось бы, чтобы этот подход работал?);

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

18 голосов | спросил heltonbiker 6 J000000Monday15 2015, 03:41:22

3 ответа


28

Как среда IDE организует вещи

Во-первых, именно так IDE организует ваш «эскиз»:

  • Основной файл .ino является тем же именем, что и папка, в которой он находится. Таким образом, для foobar.ino в foobar - основной файл - foobar.ino.
  • Любые другие .ino файлы в этой папке объединены вместе в алфавитном порядке в конце основного файла (независимо от того, где находится основной файл, в алфавитном порядке).
  • Этот конкатенированный файл становится файлом .cpp (например. foobar.cpp) - он помещается во временную папку компиляции.
  • Препроцессор «полезно» генерирует прототипы функций для функций, которые он находит в этом файле.
  • Основной файл сканируется для директив #include <libraryname>. Это заставляет IDE также копировать все соответствующие файлы из каждой (упомянутой) библиотеки во временную папку и генерировать инструкции для их компиляции.
  • Любые файлы .c, .cpp или .asm в папке эскиза добавляются в процесс сборки как отдельные единицы компиляции (это есть, они скомпилированы обычным образом в виде отдельных файлов)
  • Любые файлы .h также копируются во временную папку компиляции, поэтому их можно ссылаться на ваши файлы .c или .cpp.
  • Компилятор добавляет в стандартные файлы процесса сборки (например, main.cpp)
  • Затем процесс сборки компилирует все вышеуказанные файлы в файлы объектов.
  • Если фаза компиляции завершается успешно, они связаны вместе со стандартными библиотеками AVR (например, давая вам strcpy и т. д.)

Побочным эффектом всего этого является то, что основной эскиз (файлы .ino) можно рассматривать как C ++ во всех смыслах и целях. Однако генерация прототипа функции может привести к неясным сообщениям об ошибках, если вы не будете осторожны.


Избегайте предпроцессорных причуд

Самый простой способ избежать этих особенностей - оставить свой основной эскиз пустым (и не использовать другие файлы .ino). Затем сделайте еще одну вкладку (файл .cpp) и поместите в нее свои вещи следующим образом:

 #include <Arduino.h>

// put your sketch here ...

void setup ()
  {

  }  // end of setup

void loop ()
  {

  }  // end of loop

Обратите внимание, что вам нужно включить Arduino.h. IDE делает это автоматически для основного эскиза, но для других единиц компиляции вам нужно это сделать. В противном случае он не будет знать о таких вещах, как String, аппаратные регистры и т. Д.


Избегая установки /основной парадигмы

Вам не нужно запускать концепцию установки /цикла. Например, ваш .cpp-файл может быть:

 #include <Arduino.h>

int main ()
  {
  init ();  // initialize timers
  Serial.begin (115200);
  Serial.println ("Hello, world");
  Serial.flush (); // let serial printing finish
  }  // end of main

Включение встроенной библиотеки

Если вы используете концепцию «пустой эскиз», вам все равно нужно включить библиотеки, используемые в другом месте проекта, например, в основной файл .ino:

 #include <Wire.h>
#include <SPI.h>
#include <EEPROM.h>

Это связано с тем, что среда IDE проверяет только основной файл для использования библиотеки. Эффективно вы можете рассматривать основной файл как «проектный» файл, который назначает, какие внешние библиотеки используются.


Проблемы с именами

  • Не называйте свой основной эскиз «main.cpp» - среда IDE включает свой собственный main.cpp, поэтому вы будете иметь дубликат, если вы это сделаете.

  • Не указывайте свой .cpp-файл с тем же именем, что и ваш основной файл .ino. Так как файл .ino эффективно становится .cpp-файлом, это также даст вам столкновение имен.


  

Объявление класса C ++ в одном и том же одиночном файле .ino (слышал, но никогда не видел, что это возможно?);

Да, это компилируется ОК:

 class foo {
  public:
};

foo bar;

void setup () { }
void loop () { }

Однако вам, вероятно, лучше всего следовать обычной практике: поместите свои объявления в файлы .h и ваши определения (реализации) в .cpp (или .c).

Почему «возможно»?

Как мой пример показывает, что can помещает все вместе в один файл. Для более крупных проектов лучше быть более организованным. В конце концов вы попадаете на сцену в проекте среднего и крупного размера, где вы хотите разделить вещи на «черные ящики» - то есть класс, который делает что-то одно, делает это хорошо, проверяется и является самодостаточным ( насколько это возможно).

Если этот класс используется в нескольких других файлах в вашем проекте, это где отдельный .h и .cppфайлы вступают в игру.

  • .h file объявляет класс - то есть он предоставляет достаточно подробностей для других файлов, чтобы знать, что он делает, какие функции он имеет и как они называются.

  • Файл .cpp определяет (реализует) класс - то есть он фактически предоставляет функции и статические члены класса, которые делают класс его вещь. Поскольку вы только хотите реализовать его один раз, это находится в отдельном файле.

  • Файл .h - это то, что входит в другие файлы. Файл .cpp скомпилирован один раз IDE для реализации функций класса.

Библиотеки

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

Перезагрузите среду IDE, и теперь она знает об этой библиотеке. Это действительно тривиально просто, и теперь вы можете поделиться этой библиотекой по нескольким проектам. Я делаю это много.


Немного подробнее здесь .

ответил Nick Gammon 6 J000000Monday15 2015, 08:19:03
6

Мой совет заключается в том, чтобы придерживаться типичного способа выполнения C ++: отдельный интерфейс и реализация в файлы .h и .cpp для каждого класса.

Есть несколько уловов:

  • вам нужен хотя бы один файл .ino. Я использую символическую ссылку на файл .cpp, где я создаю классы.
  • вы должны предоставить обратные вызовы, которые ожидает среда Arduino (setu, loop и т. д.).
  • в некоторых случаях вас удивят нестандартные странные вещи, которые различают IDE Arduino от нормального, например, автоматическое включение определенных библиотек, но не другие.

Или вы можете вырвать IDE Arduino и попробовать Eclipse . Как я уже упоминал, некоторые из вещей, которые должны помочь новичкам, как правило, заходят на пути более опытных разработчиков.

ответил Igor Stoppa 6 J000000Monday15 2015, 04:50:50
6

Я отправляю ответ только для полноты, после выяснения и тестирования способа объявления и реализации класса в том же .cpp-файле без использования заголовка. Итак, в отношении точной формулировки моего вопроса «сколько типов файлов мне нужно использовать классы», в настоящем ответе используются два файла: один .ino с include, setup и loop и .cpp, содержащий целое (довольно минималистичное ) класс, представляющий сигналы поворота игрушечного автомобиля.

Blinker.ino

#include <TurnSignals.cpp>

TurnSignals turnSignals(2, 4, 8);

void setup() { }

void loop() {
  turnSignals.run();
}

TurnSignals.cpp

#include "Arduino.h"

class TurnSignals
{
    int 
        _left, 
        _right, 
        _buzzer;

    const int 
        amberPeriod = 300,

        beepInFrequency = 600,
        beepOutFrequency = 500,
        beepDuration = 20;    

    boolean
        lightsOn = false;

    public : TurnSignals(int leftPin, int rightPin, int buzzerPin)
    {
        _left = leftPin;
        _right = rightPin;
        _buzzer = buzzerPin;

        pinMode(_left, OUTPUT);
        pinMode(_right, OUTPUT);
        pinMode(_buzzer, OUTPUT);            
    }

    public : void run() 
    {        
        blinkAll();
    }

    void blinkAll() 
    {
        static long lastMillis = 0;
        long currentMillis = millis();
        long elapsed = currentMillis - lastMillis;
        if (elapsed > amberPeriod) {
            if (lightsOn)
                turnLightsOff();   
            else
                turnLightsOn();
            lastMillis = currentMillis;
        }
    }

    void turnLightsOn()
    {
        tone(_buzzer, beepInFrequency, beepDuration);
        digitalWrite(_left, HIGH);
        digitalWrite(_right, HIGH);
        lightsOn = true;
    }

    void turnLightsOff()
    {
        tone(_buzzer, beepOutFrequency, beepDuration);
        digitalWrite(_left, LOW);
        digitalWrite(_right, LOW);
        lightsOn = false;
    }
};
ответил heltonbiker 8 J000000Wednesday15 2015, 14:52:42

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132