неопределенная ссылка на функцию внутри блока ifdef

У меня есть следующая проблема:

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

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

Пример:

#define IR_SENSOR_USED

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

Пример из моего файла Sensors.h:

#ifdef IR_SENSOR_USED
  #include "SparkFun_GridEYE_AMG88/src/SparkFun_GridEYE_Arduino_Library.h"
  extern GridEYE grideye;
  void setup_ir_sensor();
  void read_ir_sensor();
  void enable_ir_interrupt(float lower, float upper, float hysteresis);
  void disable_ir_interrupt();
#endif

и из моего файла Sensors.cpp:

#ifdef IR_SENSOR_USED
void setup_ir_sensor() {
  Wire.begin(16, 17, 0x69);
  grideye.begin(0x69, Wire);
}

void read_ir_sensor() {
  for (int i = 0; i <= 64; i++) {
    sensor_values.ir_pixel_temp[i] = grideye.getPixelTemperature(i);
  }
  sensor_values.ir_device_temp = grideye.getDeviceTemperature();
}

void enable_ir_interrupt(float lower, float upper, float hysteresis) {...}

void disable_ir_interrupt() {...}
#endif

Однако, пока у меня есть #ifdef в файле .cpp, я получаю следующую ошибку, если пытаюсь вызвать функцию в setup():

sketch/Sensors.ino.cpp.o:(.literal._Z5setupv+0xc): undefined reference to `read_ir_sensor()'
sketch/Sensors.ino.cpp.o: In function `setup()':
.../Sensors/Sensors.ino:112: undefined reference to `read_ir_sensor()'
collect2: error: ld returned 1 exit status
exit status 1

Если я их закомментирую, код будет выполняться нормально. Другая функция (setup_sensors()), которая также находится в файлах Sensors.h и .cpp и не окружена #ifdef, тоже работает нормально.

Это мой Sensors.ino скетч:

#define IR_SENSOR_USED
//#define COLOR_SENSOR_USED
//#define ENV_SENSOR_USED
//#define TEMP_SENSOR_USED

#include "Sensors.h"

void setup() {
  sensor_setup();

  read_ir_sensor();
}

void loop() {
}

В чем причина этого? (Почему) препроцессор неправильно выполняет директивы в файле .cpp?


person Lithimlin    schedule 06.03.2019    source источник
comment
Вы делаете сборку с Sensors.cpp (или объектным файлом, созданным из него)? А макрос IR_SENSOR_USED определен для всех исходных файлов?   -  person Some programmer dude    schedule 06.03.2019
comment
Где находится #define IR_SENSOR_USED?   -  person AMA    schedule 06.03.2019
comment
В моем Sensors.ino у меня есть #define IR_SENSOR_USED в качестве первой строки, прежде чем у меня есть #include "Sensors.h"   -  person Lithimlin    schedule 06.03.2019
comment
Значит, он не определен при сборке Sensors.cpp? Помните, что ваши исходные файлы создаются отдельно и что директивы препроцессора (например, определения макросов) предназначены только для одного единицы перевода.   -  person Some programmer dude    schedule 06.03.2019
comment
Разве не тогда? Я предполагал, что это сработает. Если это не так, то весь мой подход несколько бесполезен, не так ли? Как еще я мог бы выполнить то, что я пытаюсь сделать?   -  person Lithimlin    schedule 06.03.2019
comment
Я не знаком с системой Arduino IDE, но обычно в IDE можно было установить макросы препроцессора в настройках проекта, и макросы будут определены для всех исходных файлов в проекте.   -  person Some programmer dude    schedule 06.03.2019
comment
Я посмотрю на это. Между тем, есть ли другой подход, который я мог бы использовать?   -  person Lithimlin    schedule 06.03.2019
comment
Вы можете сохранить свои определения в отдельном файле .h и включить его из Sensors.h. Другой вариант — задать определение для всего проекта через настройки проекта. Я не знаю, как именно это делается для проектов Arduino.   -  person AMA    schedule 06.03.2019
comment
Хм, я бы хотел, чтобы это было как можно проще (то есть только один файл, который студенты должны были бы редактировать и обрабатывать)   -  person Lithimlin    schedule 06.03.2019
comment
Я нашел этот пост, но я действительно не понимаю, что здесь делается. stackoverflow.com/questions/45393975 /   -  person Lithimlin    schedule 06.03.2019
comment
Возможно, вы могли бы поместить свои определения в Makefile, т.е. здесь?   -  person julians    schedule 06.03.2019


Ответы (2)


Как указывалось в комментариях и другом ответе, любые директивы #define видны только в файлах, которые их содержат. Итак, имея

#define IR_SENSOR_USED

в Sensors.cpp не повлияет на код, не скомпилированный в Sensors.cpp (что важно, это повлияет на код, содержащийся в .h файлах, которые включены в Sensors.cpp после #define. Но это не повлияет ни на что, содержащееся в другом .cpp файле.

Более сложные среды сборки, чем Arduino, имеют лучшие и более сложные способы решения этой проблемы, но мир Arduino не дает вам много инструментов для решения этой проблемы.

Что я делаю в мире Arduino, так это просто создаю файл с именем config.h, который содержит все операторы #define, которые мне нужны для всего проекта. Я #include "config.h" в каждом файле, которому нужны эти значения.

Итак, в вашем случае вы должны поместить все свои определения, которые показывают, какие устройства используются в config.h, а затем #include "config.h" в начале каждого файла, который от него зависит.

Вы даже можете включить его в начало файла Sensors.h. Я бы подумал о том, чтобы разделить два файла, чтобы была четкая граница между тем, что нужно настроить, и тем, что представляет собой полезный код.

Я также храню в этом файле любые конфиденциальные значения (учетные данные Wi-Fi, пароли, ключи API) и исключаю их из кода, который публикую на Github. Вместо него я включаю файл «config-example.h» со всеми директивами, но с фиктивными значениями, чтобы другие, использующие код, могли редактировать и переименовывать его.

person romkey    schedule 06.03.2019

Этот вопрос может быть примером проблемы XY.

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

#define IR_SENSOR_USED
#include "Sensors.h"

становится просто #include "IR_sensor.h".

  • Если размер библиотеки важен или вас беспокоит, можно разделить ее на отдельные библиотеки.

  • Третий вариант — предоставить функциональность в виде библиотеки только для заголовков.

Точный ответ:

В чем причина этого? (Почему) препроцессор неправильно выполняет директивы в файле .cpp?

Наиболее вероятная причина в том, что Sensors.cpp не знает о #define IR_SENSOR_USED. Define находится в другом файле, который не включен.

Однако даже если IR_SENSOR_USED будет определено в Sensors.cpp, возникает другая проблема: необходима перекомпиляция Sensors.cpp для каждой возможной комбинации определений. В противном случае ifdefed-код исключается из компиляции и не может быть просто включен на стороне клиента вызовом #define.

person AMA    schedule 06.03.2019