#c #cpp #linux
Ситуация следующая. Есть функция (в отдельном файле), поведение которой зависит от внешней переменной. Эта функция должна вызываться как из программ, в которых устанавливается значение этой переменной, так и программ, которые об этой переменной ничего не знают (и знать не хотят). Скажу сразу, что если функция оттранслирована gcc -c, то проблем нет. С ней нормально работают любые как C, так и C++ программы. Просто попробовал скомпилировать ее C++ (естественно, будет работать только с C++ программами) и возникла проблема: ext-p.o:(.bss+0x0): multiple definition of `extext' /tmp/ccLZRaiS.o:(.data+0x0): first defined here collect2: ld returned 1 exit status Вот файлы: // ext.h extern const char *extext; #ifdef __cplusplus extern "C" { #endif int ptext (const char *t); #ifdef __cplusplus } #endif Этот заголовочный файл включается во все программы, вызывающие ptext(). // ext-p.c та самая внешняя функция #include#include "ext.h" const char *extext; int ptext (const char *p) { return puts (p ? : extext ? : "nothing to print"); } Тут дело в том, что если внешний модуль не использует extext, то ее значение будет NULL. Пара программ, вызывающих функцию // ext-mc.c #include #include "ext.h" #ifndef __cplusplus const char *extext = "text test"; #else const char *extext = "c++ text test"; #endif int main (int ac, char *av[]) { ptext(av[1]); } ext-mc.c устанавливает значение etext, а следующая ничего о нем и знать не хочет. // ext-m.c #include #include "ext.h" int main (int ac, char *av[]) { ptext(av[1]); } Протокол компиляции и запуска avp@avp-xub11:avparse$ gcc ext-p.c -c avp@avp-xub11:avparse$ g++ ext-mc.c ext-p.o avp@avp-xub11:avparse$ ./a.out c++ text test avp@avp-xub11:avparse$ gcc ext-mc.c ext-p.o avp@avp-xub11:avparse$ ./a.out text test avp@avp-xub11:avparse$ g++ ext-m.c ext-p.o avp@avp-xub11:avparse$ ./a.out nothing to print avp@avp-xub11:avparse$ gcc ext-m.c ext-p.o avp@avp-xub11:avparse$ ./a.out nothing to print avp@avp-xub11:avparse$ Видно, что когда файл с ptext() оттранслирован gcc все работает, как задумано. avp@avp-xub11:avparse$ g++ ext-p.c -c avp@avp-xub11:avparse$ g++ ext-m.c ext-p.o avp@avp-xub11:avparse$ ./a.out nothing to print avp@avp-xub11:avparse$ g++ ext-mc.c ext-p.o ext-p.o:(.bss+0x0): multiple definition of `extext' /tmp/ccLZRaiS.o:(.data+0x0): first defined here collect2: ld returned 1 exit status avp@avp-xub11:avparse$ Собственно, вопрос тот же, что и в заголовке -- Кто-нибудь знает, как запрограммировать такую штуку, что бы все можно было компилировать и gcc и g++? Update 1 Судя по предложениям в некоторых комментариях наверное в вопросе я недостаточно осветил проблему. Есть функция (вообще-то не та, что приведена в примере), которая используется во многих программах и не вызывается напрямую из main. Она выводит содержимое некой структуры (один из ее параметров). Желательно, чтобы дополнительную информацию (константный для программы текст -- автор, версия и т.п.) не нужно было "тащить" от main(), где она естественным образом определена до этой функции. Если функция оттранслирована gcc и лежит в библиотеке, то все ОК. Проблем ни с Си, ни с С++ программами и способом задания в них этой информации (можно как в примере в вопросе, а можно просто в начале main написать extext = "bla-bla.." или даже прочесть из конфига и т.п.). Если же программист берет файл .c с ней и собирает программу (g++) вместе с другими своими .cpp файлами и в Makefile явно не прописывает, что ее надо компилировать gcc -c, то возникает описанная ошибка. Вопрос, а это вообще реализуется в С++ остается. Update 2 (смотрю, @mikillskegg появился) Сейчас мы видим такую картину: avp@avp-ubu1:avparse$ gcc -c ext-p.c; nm ext-p.o 0000000000000008 C extext 0000000000000000 T ptext U puts avp@avp-ubu1:avparse$ g++ -c ext-p.c; nm ext-p.o 0000000000000000 B extext 0000000000000000 T ptext U puts avp@avp-ubu1:avparse$ Может быть решение получится, если ответить на такой вопрос: Как надо писать ext-p.c, что бы в выводе nm ext-p.o вместо B extext получить C extext?
Ответы
Ответ 1
Вот это: const char *extext = "text test"; не установка значения, а определение с инициализацией. Получается у вас переменная extext определена и в ext-p.c, и в ext-mc.c. Правильным будет убрать лишние определения из ext-mc.c, и устанавливать extext где-нибудь в main: // ext-mc.c #include#include "ext.h" int main (int ac, char *av[]) { #ifndef __cplusplus extext = "text test"; #else extext = "c++ text test"; #endif int rc = ptext(av[1]); return printf ("ptext: %d\n", rc) < 0; } UPD: В С++ можно инициализировать глобалы результатом произвольного выражения. Поэтому можно сделать как-то так: bool initialized = (extext = "text"); int main() { ... } Можно так: class GlobalInitialization { GlobalInitialization() { extext = "text"; } static GlobalInitialization instance_; }; GlobalInitialization GlobalInitialization::instance_; int main() { ... } Но в C оба эти способа работать не будут. Ответ 2
Если используются сугубо ГНУтые компиляторы, то почему бы не ослабить (в компоновочном смысле) определение extext в ext-p.c: const char *extext __attribute__((weak)); Тогда и плюсовый компилятор выдаст в nm 0000000000000000 V extext И всё будет успешно работать.
Комментариев нет:
Отправить комментарий