Множественное объявление

Тема в разделе "Arduino & Shields", создана пользователем Kot26ru, 13 май 2021.

  1. Kot26ru

    Kot26ru Гик

    Приветствую. Помогите победить проблему.
    Есть основной файл .ino в котором подключены заголовки .h остальных
    Код (C++):
    #ifndef TXRX
    #define TXRX
    #include "TxRx.h"
    #include "base.h"
    #include "encoder.h"
    #include "gps.h"
    #include "led.h"
    #include "lsd.h"
    #include "radio.h"
    #include "rfid.h"
    #include "sound.h"
    #endif



    void setup()
    {

    }

    void loop()
    {

    }

    прилагаю "проблемный" файл
    Код (C++):
    #ifndef LED_H_
    #define LED_H_
    #include <Arduino.h>
    #define PIN_STRIP 8               // пин ленты
    #define NUMLEDS 24                // кол-во светодиодов
    #define COLOR_DEBTH 3
    #include <microLED.h>

    class Led
    {
      private:
     


      public:
        byte Color_LED = 50;        
        byte bright_LED = 60;        

        Led();                                  // конструктор
        void Bright(const uint8_t&);
        void Led_org();                      
        void Led_del();                      
        void Led_art(double&, unsigned long&);

        ~Led();                                 // деструктор
    };
    #endif
    Код (C++):
    #include "led.h"

    microLED<NUMLEDS, PIN_STRIP, MLED_NO_CLOCK, LED_WS2812, ORDER_GRB, CLI_LOW> strip;

    Led::Led()
    {
      strip.setBrightness(bright_LED);
      strip.clear();
      strip.show();
    }

    void Led::Bright(const uint8_t& bright_LED)
    {
      strip.setBrightness(bright_LED);
    }

    void Led::Led_org()
    {
      static byte counter = 0;
      for (int i = 0; i < NUMLEDS; i++)
      {
        strip.set(i, mWheel8(counter + i * 255 / NUMLEDS));
      }
      counter += 3;
      strip.show();
    }

    void Led::Led_del()
    {
      for (uint8_t i = 0; NUMLEDS; i++)
      {
        strip.clear();
        strip.fill(0, i, mRed);
        strip.show();
        delay(30);
      }
      strip.clear();
      strip.show();
    }

    void Led::Led_art(double& courseArt, unsigned long& distanceArt)
    {
      if (distanceArt < 50) {
        uint8_t LED;
        LED = (round (courseArt / 15));
        if (!LED) LED = 24;

        switch (round (distanceArt / 5)) {
          case 6:
          case 7:
          case 8:
          case 9:
            strip.leds[LED] = mHSV(Color_LED, bright_LED / 5, 255);
            break;
          case 3:
          case 4:
          case 5:
            strip.leds[LED] = mHSV(Color_LED, bright_LED, 255);
            if (LED == 1) {
              strip.leds[24] = mHSV(Color_LED, bright_LED / 5, 255);
              strip.leds[2] = mHSV(Color_LED, bright_LED / 5, 255);
            }
            else if (LED == 24) {
              strip.leds[23] = mHSV(Color_LED, bright_LED / 5, 255);
              strip.leds[1] = mHSV(Color_LED, bright_LED / 5, 255);
            }
            else {
              strip.leds[LED + 1] = mHSV(Color_LED, bright_LED / 5, 255);
              strip.leds[LED - 1] = mHSV(Color_LED, bright_LED / 5, 255);
            }
            break;
          case 2:
            strip.leds[LED] = mHSV(Color_LED, bright_LED, 255);
            if (LED == 1) {
              strip.leds[24] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[23] = mHSV(Color_LED, bright_LED / 5, 255);
              strip.leds[2] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[3] = mHSV(Color_LED, bright_LED / 5, 255);
            }
            else if (LED == 2) {
              strip.leds[24] = mHSV(Color_LED, bright_LED / 5, 255);
              strip.leds[1] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[3] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[4] = mHSV(Color_LED, bright_LED / 5, 255);
            }
            else if (LED == 23) {
              strip.leds[21] = mHSV(Color_LED, bright_LED / 5, 255);
              strip.leds[22] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[24] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[1] = mHSV(Color_LED, bright_LED / 5, 255);
            }
            else if (LED == 24) {
              strip.leds[22] = mHSV(Color_LED, bright_LED / 5, 255);
              strip.leds[23] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[1] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[2] = mHSV(Color_LED, bright_LED / 5, 255);
            }
            else {
              strip.leds[LED + 1] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[LED + 2] = mHSV(Color_LED, bright_LED / 5, 255);
              strip.leds[LED - 1] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[LED - 2] = mHSV(Color_LED, bright_LED / 5, 255);
            }
            break;
          case 1:
          case 0:
            strip.leds[LED] = mHSV(Color_LED, bright_LED, 255);
            if (LED == 1) {
              strip.leds[24] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[23] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[2] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[3] = mHSV(Color_LED, bright_LED, 255);
            }
            else if (LED == 2) {
              strip.leds[24] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[1] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[3] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[4] = mHSV(Color_LED, bright_LED, 255);
            }
            else if (LED == 23) {
              strip.leds[21] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[22] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[24] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[1] = mHSV(Color_LED, bright_LED, 255);
            }
            else if (LED == 24) {
              strip.leds[22] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[23] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[1] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[2] = mHSV(Color_LED, bright_LED, 255);
            }
            else {
              strip.leds[LED + 1] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[LED + 2] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[LED - 1] = mHSV(Color_LED, bright_LED, 255);
              strip.leds[LED - 2] = mHSV(Color_LED, bright_LED, 255);
            }
        }
      }
      else strip.clear();
    }
    Компилятор выдает следующие ошибки
    Arduino: 1.8.13 (Windows 10), Плата:"Arduino Uno"
    sketch\led.cpp.o (symbol from plugin): In function `getFade(mData, unsigned char)':
    (.text+0x0): multiple definition of `getFade(mData, unsigned char)'
    sketch\TxRx.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
    sketch\led.cpp.o (symbol from plugin): In function `getFade(mData, unsigned char)':
    (.text+0x0): multiple definition of `getHEX(mData)'
    sketch\TxRx.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
    sketch\led.cpp.o (symbol from plugin): In function `getFade(mData, unsigned char)':
    (.text+0x0): multiple definition of `getBlend(int, int, mData, mData)'
    sketch\TxRx.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
    sketch\led.cpp.o (symbol from plugin): In function `getFade(mData, unsigned char)':
    (.text+0x0): multiple definition of `mRGB(unsigned char, unsigned char, unsigned char)'
    sketch\TxRx.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
    sketch\led.cpp.o (symbol from plugin): In function `getFade(mData, unsigned char)':
    (.text+0x0): multiple definition of `mHSVfast(unsigned char, unsigned char, unsigned char)'
    sketch\TxRx.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
    sketch\led.cpp.o (symbol from plugin): In function `getFade(mData, unsigned char)':
    (.text+0x0): multiple definition of `mHSV(unsigned char, unsigned char, unsigned char)'
    sketch\TxRx.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
    sketch\led.cpp.o (symbol from plugin): In function `getFade(mData, unsigned char)':
    (.text+0x0): multiple definition of `mHEX(unsigned long)'
    sketch\TxRx.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
    sketch\led.cpp.o (symbol from plugin): In function `getFade(mData, unsigned char)':
    (.text+0x0): multiple definition of `mWheel(int, unsigned char)'
    sketch\TxRx.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
    sketch\led.cpp.o (symbol from plugin): In function `getFade(mData, unsigned char)':
    (.text+0x0): multiple definition of `mWheel8(unsigned char, unsigned char)'
    sketch\TxRx.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
    sketch\led.cpp.o (symbol from plugin): In function `getFade(mData, unsigned char)':
    (.text+0x0): multiple definition of `mKelvin(int)'
    sketch\TxRx.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
    sketch\led.cpp.o (symbol from plugin): In function `getFade(mData, unsigned char)':
    (.text+0x0): multiple definition of `systemUptimePoll()'
    sketch\TxRx.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
    collect2.exe: error: ld returned 1 exit status
    exit status 1
    Ошибка компиляции для платы Arduino Uno.
    Если закоментить строку в TxRx.ino (//#include "led.h"), то компиляция проходит успешно.
     
    Последнее редактирование: 13 май 2021
  2. Asper Daffy

    Asper Daffy Иксперд

    А где файл "microLED.h"?
     
  3. Kot26ru

    Kot26ru Гик

    Последнее редактирование: 13 май 2021
  4. b707

    b707 Гуру

    вы хотя бы ссылку выложите, откуда вы ее скачивали
     
  5. Kot26ru

    Kot26ru Гик

    поправил пост выше, сорри:)
     
  6. b707

    b707 Гуру

    Класс Led описан. а где он используется?
     
  7. Kot26ru

    Kot26ru Гик

    будет использоваться в основном файле (TxRx.ino). Я логику еще не прописывал.Именно после "прикручивания" led.h в TxRx.ino и появляется ошибка.
     
  8. Asper Daffy

    Asper Daffy Иксперд

    Ну, если Вы пользуетесь библиотеками от гивера, то, обычно, медицина бессильна.

    Проблема в том, что файл "color_utility.h" нельзя включать в два разных .cpp файла, а у Вас он включается (не обязательно Вы сами включали, но он включён).

    Исправить можно двумя способами. Либо не включать, либо поправить его следующим образом:

    1. В прототипах функций в строках №№98-108 перед каждой написать слово extern.
    2. определения этих функций (строки №№ 209-392 из этого файла убрать и перенести в отдельный файл color_utility.cpp в котором в начале обязательно прописать #include <Arduino.h> и #include "color_utility.h"

    Возможно, потребуется что-то ещё прописать. Надо сделать это, а там видно будет.
     
  9. Kot26ru

    Kot26ru Гик

    Спасибо, сейчас попробую подправить библу
     
  10. Kot26ru

    Kot26ru Гик

    Спасибо большое! Помогло!!!:)
    p.s. в microLED.h так же нужно сделать systemUptimePoll () -> extern и вынести в .cpp ее определение
     
  11. Asper Daffy

    Asper Daffy Иксперд

    Ну, Вы поняли суть проблемы. Если функции определены в .h файле, и этот файл включается в два разных .cpp файла, то получается, что функция определена в двух местах. Привет гиверу.
     
    Andrey12 нравится это.
  12. AlexU

    AlexU Гуру

    Это не нужно.
    А вот декларацию массива '_CRTgammaPGM' (строки 123 - 140) нужно вынести в 'color_utility.cpp'. Иначе многократно будет выделяться память под массив (зависит от '#include').
    Плюс переопределение операторов (строки 31 - 36), так же в 'color_utility.cpp'.
    Но все эти "выносы" нужно делать аккуратно с учётом условной компиляции (см. #if, #elif и т.п.).
     
  13. Asper Daffy

    Asper Daffy Иксперд

    Я считаю, что нужно.
     
  14. AlexU

    AlexU Гуру

    Да считать можно до скольки угодно.
    Вот только 'extern' у функции имеет смысл только в том случае, если нужно использовать функцию, написанную на другом языке программирования.
    Например, в программе на языке 'C++' нужно использовать функцию на языке 'C'. В этом случае нужно функцию декларировать примерно следующим образом:
    Код (C++):

    extern "C" mData getFade(mData data, uint8_t val); // В 'C++' коде будет использоваться функция 'getFade', написанная на языке 'C'
     
    А если все файлы написаны только на языке 'C++', то 'extern' у деклараций сигнатур функций не нужен.
     
  15. SergeiL

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

    Не, это связано только с указанием на место определения функции, впрочем как и переменной.
    Это и до С++ было, если функция или переменная объявлена в другом файле, указываем ее как "external", чтобы не было повторного определения. Я С++ только поверхностно знаю, но на Си так же было.
     
    Последнее редактирование: 13 май 2021
  16. Рокки1945

    Рокки1945 Гуру

    ну да декларация переменной
     
  17. AlexU

    AlexU Гуру

    Какого места?
    В стандарт что-ли гляньте.
    Для переменной модификатор 'extern' говорит компилятору -- "есть переменная такого-то типа с таким-то идентификатором, но место под неё выделять не надо, место будет выделено в другом исходнике, во время линковки узнаешь где".
    А для функции 'extern' что будет означать? Когда простое определение сигнатуры функции без 'extern' и так уже говорит -- "где-то есть функция, которую вызывать нужно так-то".
    Какую дополнительную информацию будет нести 'extern' для компилятора при определении сигнатуры функции?
    Эти вопросы касаются как языка 'C', так и 'C++'.

    Как уже сказал выше, для функции модификатор 'extern' имеет смысл только при декларации сигнатуры функции, написанной на другом языке программирования.
    Добавить 'extern' к сигнатурам функций конечно можно, но это будет бессмысленная работа.
     
  18. SergeiL

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

    Может ошибаюсь, это было лет 30 назад, но когда мы указываем "extern" мы говорим компилятору, что есть такая функция с такими параметрами на стеке и нужно это закинуть на стек, а это получить как возвращаемый параметр. Как частный случай. При этом, компилятор нам верит, а линкер соединяет все корректно с определенными параметрами.
     
  19. AlexU

    AlexU Гуру

    Правильней будет сказать; когда объявляем сигнатуру функции (без 'extern'), то мы говорим компилятору -- "что есть такая-то функция с такими-то параметрами; параметры в функцию передаются таким-то образом (через стек, регистры и т.п.); которая возвращает значение такого-то типа (опять же, определяется как будет возвращаться значение) или ни чего не возвращает; где находится реализация функции пока не знаем; во время линковки линкер даст нам её адрес".
    С 'extern' будет ровно то же самое.
    А вот с 'extern "имя_ЯП"' мы можем изменить порядок вызова функции.
     
  20. Asper Daffy

    Asper Daffy Иксперд

    Ну, так, гляньте же. Кто же Вам не даёт?
    Вы тут ничего "в кучу не смешали"? Слово extern в С++ используется в трёх контекстах: как "storage-class-specifier", "linkage-specifications" и в "explicit-instantiations". Вы, здесь, очевидно, о втором. Но разговор-то не о нём, так что давайте забудем про другие языки.
    Да, в самых простейших случаях так и будет. А вот в чуть более сложных ... беда. Кончено, скорее всего, умный компилятор/линкер разберутся и сделают всё как надо (ну, а не разберутся, значит не попёрло :-( ), но изматерятся на страницу!

    Примеров таких "более сложных" случаев можно привести немало. Самый простой - если функция создаётся из шаблона.

    Так что "'extern' у функции имеет смысл" всегда. Там где он необходим - понятно, а там где "без разницы" - хуже от него не становится. А потому, если функция определена в другом файле - писать extern просто общеполезная привычка - не помешает никогда, а в каких-то случаях поможет.
     
    Последнее редактирование: 14 май 2021
    SergeiL и Andrey12 нравится это.