The situation is as follows.

There is a function (in a separate file) whose behavior depends on the external variable. This function should be called from programs that set the value of this variable, and programs that do not know anything about this variable (and do not want to know).

I have to say that if the function is translated by gcc -c, then there is no problem. Any C as well as C ++ programs normally work with it.

I just tried to compile it with C ++ (of course, it will work only with C ++ programs) and there was a problem:

ext-po:(.bss+0x0): multiple definition of `extext' /tmp/ccLZRaiS.o:(.data+0x0): first defined here collect2: ld returned 1 exit status 

Here are the files:

 // ext.h extern const char *extext; #ifdef __cplusplus extern "C" { #endif int ptext (const char *t); #ifdef __cplusplus } #endif 

This header file is included in all programs that call ptext() .

 // ext-pc та самая внешняя функция #include <stdio.h> #include "ext.h" const char *extext; int ptext (const char *p) { return puts (p ? : extext ? : "nothing to print"); } 

The point is that if the external module does not use extext, then its value will be NULL.

A couple of programs calling the function.

 // ext-mc.c #include <stdio.h> #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 sets the value to etext , and the next one doesn't want to know anything about it.

 // ext-mc #include <stdio.h> #include "ext.h" int main (int ac, char *av[]) { ptext(av[1]); } 

Compilation and Run Protocol

 avp@avp-xub11:avparse$ gcc ext-pc -c avp@avp-xub11:avparse$ g++ ext-mc.c ext-po avp@avp-xub11:avparse$ ./a.out c++ text test avp@avp-xub11:avparse$ gcc ext-mc.c ext-po avp@avp-xub11:avparse$ ./a.out text test avp@avp-xub11:avparse$ g++ ext-mc ext-po avp@avp-xub11:avparse$ ./a.out nothing to print avp@avp-xub11:avparse$ gcc ext-mc ext-po avp@avp-xub11:avparse$ ./a.out nothing to print avp@avp-xub11:avparse$ 

It can be seen that when the file with ptext() translated by gcc everything works as intended.

 avp@avp-xub11:avparse$ g++ ext-pc -c avp@avp-xub11:avparse$ g++ ext-mc ext-po avp@avp-xub11:avparse$ ./a.out nothing to print avp@avp-xub11:avparse$ g++ ext-mc.c ext-po ext-po:(.bss+0x0): multiple definition of `extext' /tmp/ccLZRaiS.o:(.data+0x0): first defined here collect2: ld returned 1 exit status avp@avp-xub11:avparse$ 

Actually, the question is the same as in the title - Does anyone know how to program such a thing , so that everything can be compiled and gcc and g ++?

Update 1

Judging by the suggestions in some of the comments, probably in the question I didn’t highlight the problem enough.

There is a function (actually not the one shown in the example), which is used in many programs and is not called directly from main. It displays the contents of a certain structure (one of its parameters).

It is desirable that the additional information (constant for the program text - the author, version, etc.) does not need to be “dragged” from main (), where it is naturally defined before this function.

If the function is translated by gcc and is in the library, then everything is OK. There are no problems with C, nor with C ++ programs and the way of specifying this information in them (as in the example in question, you can just write main at the beginning of the text extext = "bla-bla .." or even read from the config, etc. P.).

If the programmer takes the .c file with it and collects the program (g ++) along with his other .cpp files and does not explicitly state in the Makefile that it should be compiled with gcc -c, then the described error occurs.

The question, and this is generally implemented in C ++, remains.

Update 2 (look @mikillskegg appeared)

Now we see the following picture:

 avp@avp-ubu1:avparse$ gcc -c ext-pc; nm ext-po 0000000000000008 C extext 0000000000000000 T ptext U puts avp@avp-ubu1:avparse$ g++ -c ext-pc; nm ext-po 0000000000000000 B extext 0000000000000000 T ptext U puts avp@avp-ubu1:avparse$ 

Maybe the solution will work if you answer this question:

How do you write ext-pc , so that in the output nm ext-po instead of B extext you get C extext ?

  • I don’t see that ext.h would have guards. - KoVadim
  • @KoVadim, it's clear that this is a minimized example. - avp
  • Most likely you already know this option and it doesn’t suit you, but I’d venture to suggest: what doesn’t suit #define extext if it’s still a constant? - MDJHD
  • @MDJHD, well, #define has nothing to do with it. Probably in the question I did not highlight the problem. There is a function (actually not the one shown in the example), which is used in many programs and is not called directly from main . It displays the contents of a certain structure (one of its parameters). It is desirable that the additional information (constant for the program text - the author, version, etc.) does not need to be “dragged” from main() , where it is naturally defined before this function. - (there is little space, I will continue in the next commentary) - avp
  • 2
    @avp, C and C ++ are different PLs, so if a developer tries to compile C files with a C ++ compiler, then these are his problems. - dzhioev

2 answers 2

This:

 const char *extext = "text test"; 

not setting a value, but a definition with initialization. It turns out that the extext variable extext defined both in ext-pc and ext-mc.c. The correct thing is to remove the extra definitions from ext-mc.c, and install extext somewhere in main:

 // ext-mc.c #include <stdio.h> #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 : In C ++ you can initialize globals with the result of an arbitrary expression. Therefore, you can do something like this:

 bool initialized = (extext = "text"); int main() { ... } 

It is possible so:

 class GlobalInitialization { GlobalInitialization() { extext = "text"; } static GlobalInitialization instance_; }; GlobalInitialization GlobalInitialization::instance_; int main() { ... } 

But in C, both of these methods will not work.

  • @zhioev, this is the obvious way. I would like in the form of a definition with initialization (outside the body of the function, more precisely, before main() ). Naturally, initialization is permissible only in one place of the load module. Those. as in Si. In crosses, is it possible in principle? - avp
  • @avp, read the question carefully. I honestly can not understand why you have g++ ext-mc.c ext-po and g++ ext-mc.c ext-po do not fall in the first two cases. The idea behind linking should be an error, because ext-po and object derived from ext-mc.c will have the same characters. - dzhioev
  • @avp, then it is worth removing the extext definition from ext-pc, but this will force you to define extext in some other module. - paulgri
  • @dzhioev, do not quite understand what the first 2 cases you are talking about. In general, we have the following: gcc -c ext-pc ext-mc.c; nm ext-po ext-mc.o ext-po: 00000004 C extext .... ext-mc.o: 00000000 D extext .... g ++ -c ext-pc ext-mc.c; nm ext-po ext-mc.o ext-po: 00000000 B extext .... ext-mc.o: 00000000 D extext .... You see the types of the external symbol extext different. Type C is the Common section. (for more details, for example, in man nm ). Rjhjxt? djghjc - f rfr hf, jnfnm c dytiybvb cbvdjkfvb d rhtcnf [& - jcnftncz / - avp
  • @avp, of course, did not know that there is such a mechanism. And why are you against initializing the outer extext in main? Your way looks weird. - dzhioev

If strictly GNT compilers are used, then why not weaken ( in the layout sense ) the definition of extext in ext-pc :

 const char *extext __attribute__((weak)); 

Then the plus compiler will produce in nm

 0000000000000000 V extext 

And everything will work successfully.