Поместите их в отдельные файлы и укажите один файл .c для обычного использования и один файл .c для тестирования.
В качестве альтернативы, #define
тестирование в командной строке с использованием тестовых сборок и использование чего-то вроде:
int main(int argc, char *argv[])
{
#ifdef TESTING
return TestMain(argc, argv);
#else
return NormalMain(argc, argv);
#endif
}
int TestMain(int argc, char *argv[])
{
// Do testing in here
}
int NormalMain(int argc, char *argv[])
{
//Do normal stuff in here
}
Другие ответы здесь вполне разумны, но, строго говоря, проблема, которая у вас есть, на самом деле связана не с GCC, а со средой выполнения C. Вы можете указать точку входа в свою программу, используя флаг -e
для ld
. В моей документации сказано:
-e имя_символа
Определяет точку входа основного исполняемого файла. По умолчанию имя записи - "start", которое находится в crt1.o, который содержит связующий код, который необходимо настроить и вызвать main ().
Это означает, что вы можете переопределить точку входа, если хотите, но вы можете не захотеть делать это для программы C, которую вы собираетесь нормально запускать на своем компьютере, поскольку start
может работать со всеми типами ОС конкретные вещи, которые требуются перед запуском вашей программы. Если вы можете реализовать свой собственный start
, вы можете делать все, что хотите.
Вы можете использовать макросы, чтобы переименовать одну функцию в главную.
#ifdef TESTING
#define test_main main
#else
#define real_main main
#endif
int test_main( int argc, char *argv[] ) { ... }
int real_main( int argc, char *argv[] ) { ... }
Я предполагаю, что вы используете Make или что-то подобное. Я бы создал два файла, которые содержат разные реализации основной функции, а затем в make-файле определите две отдельные цели, которые имеют идентичные зависимости от остальных ваших файлов, за исключением того, что один использует ваш «основной модуль модульного теста», а другой - «обычный основной». ". Примерно так:
normal: main_normal.c file1.c file2.c
unittest: main_unittest.c file1.c file2.c
Пока «нормальная» цель находится ближе к началу make-файла, то при вводе «make» она будет выбрана по умолчанию. Вам нужно будет ввести «make unittest», чтобы создать тестовую цель.
Прежде всего, вы не можете иметь две функции с именем main
в одной компиляции, так что либо исходники находятся в разных файлах, либо вы используете условную компиляцию. В любом случае вы должны получить два разных .o файла. Поэтому вам не нужна опция компоновщика; вы просто передаете нужный вам .o файл в качестве аргумента.
Если вам это не нравится, вы можете делать причудливые вещи с dlopen()
для извлечения main
из любого объектного файла, который вы назовете динамически. Я могу представить себе обстоятельства, в которых это может быть полезно - скажем, вы используете систематический подход к модульным тестам, просто помещаете их все в каталог, и ваш код ходит по каталогу, захватывая каждый объектный файл, динамически загружая его и запуская свои тесты. Но для начала, вероятно, стоит использовать что-то более простое.
Я бы предпочел использовать разные файлы и make для тестовых и производственных сборок, но если у вас есть файл с
int test_main (int argc, char*argv[])
и
int prod_main (int argc, char*argv[])
, то параметры компилятора для выбора того или другого в качестве основного будут -Dtest_main = main
и -Dprod_main = main
#ifdef TESTING
int main()
{
/* testing code here */
}
#else
int main()
{
/* normal code here */
}
#endif
$ gcc -DTESTING = 1 -o a.out filename.c # сборка для тестирования
$ gcc -UTESTING -o a.out filename.c # сборка для обычных целей
man gcc
показал мне -D и -U
Изменить: Билли опередил меня в ответе, но здесь немного больше предыстории
Если говорить более конкретно, main обычно больше является функцией стандартной библиотеки. main
вызывает не C, а стандартную библиотеку.ОС загружает приложение, передает управление точке входа библиотеки ( _start
в GCC), и библиотека в конечном итоге вызывает main
. Вот почему точкой входа для приложения Windows может быть WinMain
, а не обычная. Во встроенном программировании может быть то же самое. Если у вас нет стандартной библиотеки, вы должны написать точку входа, которую обычно предоставляет библиотека (помимо прочего), и вы можете назвать ее как хотите.
В инструментальной цепочке GCC вы также можете заменить точку входа библиотеки своей собственной, используя параметр -e
. (В этом отношении вы также можете полностью удалить библиотеку.)
Создайте свою:
int main(int argc, char *argv[])
{
#if defined(BUILD_UNIT_TESTS)
return main_unittest(argc, argv);
#endif
#if defined(BUILD_RUNTIME)
return main_run(argc, argv);
#endif
}
Если вам не нравится ifdef
, напишите два основных модуля, которые содержат только main. Свяжите один вход для модульных тестов, а другой - для обычного использования.
Для их использования вам возможно придется запускать ld
отдельно, но ld поддерживает скрипты для определения многих аспектов выходного файла (включая точку входа).