#c #include #linker-errors
#c #включить #компоновщик-ошибки
Вопрос:
Я пытаюсь понять ifdef
директиву , включая файлы, и, хотя мне показалось, что это имеет смысл в моей голове, кажется, это не так просто, как я думал: я получаю ошибку «несколько определений».
То, что я пытаюсь сделать, это заставить компилятор включить определенный файл в зависимости от того, определено ли что-то.
У меня есть a.cpp
, и b.cpp
оба из них имеют одну и ту же функцию, но немного другую логику:
a.cpp
int aFunction(int apples){
return apples * 10;
}
b.cpp
int aFunction(int apples){
return apples * 0.1;
}
В основном я пытаюсь это сделать:
#ifdef USE_B
#pragma message "Using b"
#include "b.cpp"
#else
#pragma message "Using a"
#include "a.cpp"
#endif
Затем в моем реальном коде вызовите его
aFunction(5);
Я предполагал, что компилятор прочитает эти директивы и выберет либо a.cpp
или b.cpp
, и, таким образом, соответствующий исходный код будет содержать только реализацию / экземпляр aFunction()
.
Но это делает что-то, что я не совсем понимаю, и приводит к ошибке «несколько определений».
Подробнее об ошибке ниже:
sketchb.cpp.o (symbol from plugin): In function `aFunction(int)':
(.text 0x0): multiple definition of `aFunction(int)'
sketcha.cpp.o (symbol from plugin):(.text 0x0): first defined here
Не совсем уверен, почему он даже смотрит на a.cpp
то, когда USB_B
определено.
Я определил это, перейдя:
#define USE_B 1
(Примечание: не уверен, имеет ли это значение, но я работаю в среде arduino).
Добавлены мысли / рассуждения
Кажется, я немного запутался в процессах предварительной обработки, компиляции и компоновки. Я признаю, что это не то, с чем я слишком хорошо знаком. (Спасибо за очистку моих тегов, которые напомнили мне об этом)
Для меня это работает и соответствует моим целям:
#define USE_B 1
#ifdef USE_B
#pragma message "Using b"
int aFunction(int apples){ return apples * 0.1; } // contents of b.cpp
#else
#pragma message "Using a"
int aFunction(int apples){ return apples * 10; } // contents of a.cpp
#endif
void main(){
aFunction(5);
}
Если я определяю USE_B , то только версия ‘b’ aFunction
попадает в сгенерированный источник (я предполагаю, после процесса препроцессора) и, таким образом, приводит к тому, что компилируется только одна из версий aFunction
.
Насколько я понимаю, #include
директива работает так, как если бы она «копировала и вставляла» содержимое включенного файла туда, где #include
находится директива. Возможно, я здесь ошибаюсь? Это в основном то, откуда возникает моя путаница, если #include
работать путем копирования содержимого a.cpp
и b.cpp
в мой основной файл, разве я не должен получить что-то похожее на вышеупомянутое? (что, опять же, отлично работает для меня)
Комментарии:
1. вы также безоговорочно связываете оба a.cpp и b.cpp .
2. Чего вы пытаетесь достичь (помимо выбора одной версии
aFunction
)?3. Как правило, не включайте файлы source (
.cpp
) . Используйте какой-нибудь инструмент управления проектами для условной сборки файлов.4. В качестве вероятной причины вашей проблемы препроцессор и его макросы являются отдельным языком от C , и как таковой он на самом деле не имеет операторов. Это означает, что вы не завершаете макросы точкой с запятой.
5. Если вы посмотрите на сообщение об ошибке, оба
a.cpp
иb.cpp
используются для сборки вашего исполняемого файла. Вы не только включаете файлы, вы также создаете объектные файлы из этих исходных файлов и связываетесь с объектными файлами. Вам нужно выбрать один подход (где моя рекомендация — условное построение и связывание, а не включение).
Ответ №1:
Что вы можете сделать вместо #include
редактирования исходных файлов, так это тот же трюк, который Microsoft использует для привязки к их двум версиям API ( xxxA()
, xxxW()
функции):
#ifdef USE_B
#pragma message "Using b"
#define aFunction aFunctionB
#else
#pragma message "Using a"
#define aFunction aFunctionA
#endif
и в реализации
a.cpp
int aFunctionA(int apples){
return apples * 10;
}
b.cpp
int aFunctionB(int apples){
return apples * 0.1;
}
и в вашем реальном коде вызовите его
aFunction(5);
Комментарии:
1. Будут ли версии
aFunctionA
иaFunctionB
тогда будут выполнены? В конце я пытаюсь ограничить объем использования пространства / памяти. Мне это нравится, хотя, по крайней мере, это решает проблему именования.2. @mitim Это должно регулироваться в вашей системе сборки, а не в коде. В любом случае компоновщик удалит невыбранные символы, но вам все равно придется потратить время компиляции, используемое для обеих реализаций, если вы не разберетесь с этим на этапе компиляции.
Ответ №2:
После некоторого изучения кажется, что во всем процессе есть что-то, чего я не понимаю. В любом случае, также кажется, что консенсус (из ответов здесь и того, что я могу прочитать в другом месте) заключается в том, что использование препроцессора на самом деле не является правильным способом делать то, что я хочу, и следует использовать систему сборки.
По общему признанию, это все еще немного странно на мой взгляд, но, возможно, однажды я это увижу. Поскольку проект небольшой, я думаю, я просто напишу для этого простой скрипт сборки.