РЕШЕНО директивы препроцессора

Тема в разделе "ESP8266, ESP32", создана пользователем X-Dron, 19 окт 2019.

  1. X-Dron

    X-Dron Гик

    Приветствую.
    Уже голову сломал, перепробовал все варианты, но так и не нашел решения.
    Проект для ESP32 в Visual Studio Code.
    Проект собирается без ошибок, но директивы препроцессора работают некорректно. Прошу помощи.
    Начало main.cpp. Перед включением заголовков библиотеки BMP280.h создаю два макроопределения PIN_I2C_SDA и PIN_I2C_SCL для переопределения пинов шины I2C
    Код (C++):
    #include <Arduino.h>

    #define PIN_I2C_SDA 22
    #define PIN_I2C_SCL 23
    #include "../lib/BMP280/BMP280.h"
    В BMP280.h делаю проверку макроопределений PIN_I2C_SDA и PIN_I2C_SCL. Если они не определены, то определяю их -1 (для wire.begin() это признак использования стандартного пина).
    Код (C++):
    #ifndef PIN_I2C_SDA
        #define PIN_I2C_SDA -1
    #endif

    #ifndef PIN_I2C_SCL
        #define PIN_I2C_SCL -1
    #endif
    При этом среда разработки подсказывает, что макроопределения присутствуют и #define затемнены.
    upload_2019-10-19_21-17-57.png
    это последние строки BMP280.h

    И вот начало BMP280.cpp
    Код (C++):
    #include "BMP280.h"

    bool BMP280::init(void)
    {
       Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL);

      if(bmp280Read8(BMP280_REG_CHIPID) != 0x58)
        return false;
    ........
    И вот здесь вот PIN_I2C_SDA и PIN_I2C_SCL имеют значения -1
    upload_2019-10-19_21-29-46.png
    И, соответственно, шина работает на дефолтных пинах. Реально работает.
    Какого черта???
    Запись
    Wire.begin(22, 23);
    Переназначает пины, и тоже все работает нормально. Но хочется сделать библиотеку в которую лазать для переназначения шины не нужно.

    Что я делаю не так, почему в BMP280.h и BMP280.cpp значения макроопределений разные?

    И еще немного информации.
    Если в BMP280.h сделаю полное комментирование проверки определений.
    Код (C++):
    //#ifndef PIN_I2C_SDA
    //    #define PIN_I2C_SDA -1
    //#endif

    //#ifndef PIN_I2C_SCL
    //    #define PIN_I2C_SCL -1
    //#endif
    , то компилятор выдает ошибку
    Код (C++):
    Compiling .pio\build\nodemcu-32s\lib406\BMP280\BMP280.cpp.o
    lib\BMP280\BMP280.cpp: In member function 'bool BMP280::init()':
    lib\BMP280\BMP280.cpp:6:14: error: 'PIN_I2C_SDA' was not declared in this scope
       Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL);
                  ^
    lib\BMP280\BMP280.cpp:6:27: error: 'PIN_I2C_SCL' was not declared in this scope
       Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL);
                               ^
    *** [.pio\build\nodemcu-32s\lib406\BMP280\BMP280.cpp.o] Error 1
    И кросс-референс по макроопределениям
    upload_2019-10-19_22-3-48.png

    Я догадываюсь из-за чего это происходит. (библиотека компилится до компиляции кода программы и на момент сборки библиотеки макроопределений нет). Но как выйти из этой ситуации???
     
    Последнее редактирование: 19 окт 2019
  2. Asper Daffy

    Asper Daffy Иксперд

    Всё правильно, так и должно быть.

    Что именно тебя смущает? Ты ведь свои определения вставил в основном файле. А в файле BMP280.cpp ты их не вставлял. Так откуда им там взяться?

    Прости, я просто не понял проблемы.
     
  3. b707

    b707 Гуру

    вставить свои дефайны не в скетч, а в файл BMP280.h

    нет, не поэтому. Определения в коде и в библиотеке независимы друг от друга. Подгружается только то, что явно указано во включаемых файлах. Поэтому не важно в каком порядке компилится скетч и библиотеки, определения из скетча все равно в библиотеку не попадут, если их непосредственно туда не вставить
     
    Последнее редактирование модератором: 20 окт 2019
  4. X-Dron

    X-Dron Гик

    Но они есть в BMP280.h
    Да, я уже понял в чем ошибка.
    Меня ввела в заблуждение одна из статей и я повелся. Если найду дам ссылку.

    Буду переписывать либу с параметрами для функции.
     
  5. Asper Daffy

    Asper Daffy Иксперд

    Ты просто запутался. Давай-ка я расскажу тебе, как на самом деле устроена эта жизнь.

    Тебе нужно твёрдо знать две вещи:

    №1.
    Каждый файл «.cpp» («.c», «.ino» и т.п.) компилируется сам по себе, независимо от других. Компилятор вообще не знает ничего о других файлах. Их можно компилировать в любом порядке. В одно целое код собирается позже, и этим занимается не компилятор, а компоновщик. Итак, запомни, каждый файл компилируется сам по себе и ничего не знает о других.

    №2.
    Директива #include означает «вставить в это место содержимое указанного файла». Всё. Больше никакой мистики. Это ровно тоже самое, как вставить это содержимое туда руками.

    А теперь, когда ты это знаешь, проанализируй свою ситуацию и ты легко поймёшь, что всё у тебя работает правильно.

    Если ты вставил свои определения в .ino файле, то они коснуться только включения .h файла в этот .ino, и никаким боком не коснутся включения в другом .cpp файле.
     
    Andrey12, X-Dron, Daniil и ещё 1-му нравится это.
  6. b707

    b707 Гуру

    это другие дефайны, хоть и называются так же. У дефайнов также есть область видимости, как и у переменных
    вас же не удивляет, что две переменные int a, описанные в разных функциях - независимы друг от друга?
     
  7. parovoZZ

    parovoZZ Гуру

    Я создаю отдельный файл *.h и все глобальные определения прописываю там.
     
  8. b707

    b707 Гуру

    в описанном случае этого будет недостаточно, автор хочет внедрить свои определения в чужую библиотеку,
     
  9. X-Dron

    X-Dron Гик

    Не вариант. Задача стоит сделать библиотечный класс, вообще не зависящий от прикладной задачи, не требующий изменений внутри библиотечных файлов и храниться на Git. И что будет выдавать компилятор, если в библиотеку будет включен неизвестный *.h-файл.
    Все верно, хочу сделать ее более функциональной.
    Если в BMP280.cpp есть #include BMP280.h и где дифы прописаны, то это будут одни и те же дифы. Со второй частью полностью согласен.

    Если файлы входят в дерево сборки и имеют наследования, то знает.
    main.h
    Код (C++):
    #define PIN_I2C_SDA 22
    #define PIN_I2C_SCL 23
    BMP280.h
    Код (C++):
    #include main.h
    #ifndef PIN_I2C_SDA
        #define PIN_I2C_SDA -1
    #endif

    #ifndef PIN_I2C_SCL
        #define PIN_I2C_SCL -1
    #endif
    BMP280.ccp
    Код (C++):
    #include "BMP280.h"
    bool BMP280::init(void)
    {
      Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL);
    }
    Будет работать правильно, и сборка BMP280.ccp будет зависеть от макроопределений в main.h. Но как я сказал выше это не вариант
     
  10. b707

    b707 Гуру

    только потому, что вы включили свой main.h в библиотеку.
     
  11. parovoZZ

    parovoZZ Гуру

    Почему неизвестный? Вариантов, когда Настройки выносятся в отдельный файл, полно. Компилятор ничего не скажет - он включит этот файл везде, где есть на него инклюды.
    У меня был случай, когда дефайны первого файла ссылались на инклюды второго файла, а те на первый. Получался замкнутый круг. Без стороннего файла эту задачу не решить в принципе.
     
  12. parovoZZ

    parovoZZ Гуру

    Ну и пусть добавляет. Возьми библиотеку LUFA - именно так там и сделано.
     
  13. X-Dron

    X-Dron Гик

    Переделал под параметры функции инициализации.
    Осталась проблема монопольного занятия шины I2C, но это уже совсем другая история, и направление в котором копать так же понятно.
    Закрыто.
     
    Последнее редактирование: 20 окт 2019
  14. Если не смущают накладные расходы на дополнительные вызовы (например для получения параметров), можете использовать в библиотеке "слабые" функции, а в конкретном проекте реализовать эти функции.
    Т.о. библиотека будет использовать реализацию из проекта если она есть, если её нет, использовать свои "слабые" функции, и никаких дефайнов и инклюдов зависимых от проекта.
     
  15. Asper Daffy

    Asper Daffy Иксперд

    Может проще пронаследоваться от класса библиотеки и заменить что нужно? Тогда хоть от обновления библиотек зависеть не будешь.
     
  16. b707

    b707 Гуру

    проще - но редко получается. Чаще всего как раз те переменные и методы. что хочется подправить - описаны как приватные.