> If you put static (non-const) > variables in your header files, you have misunderstood how to use header > files in C programming.
Not me, and usually const, but people do it, both. Even the consts can get duplicated. Even the simple C++ const int one = 1; I can take the address of. It is also unusual and maybe dumb. There are too many programmers writing C and C++ with too little oversight to rule these out. The static prohibition might be too closed to identity. I understand about the local edit, but it behooves one to make the non-edited build debugable too. I know you can only go so far, can't litter it with printf or breakpoints or volatile, nor can compile it unoptimized and ship it, but uniquifying function/data names seems maybe affordable for debuggability of official builds. > If you want to switch from C to C++, that's fine by me But the rest of my team has to agree. > C++ gives you namespaces, which gives you > nother way to group your identifiers and control their scope. I know *all* about C++ but I think for newbies it is best to start out focusing on member functions. Pretend there is a type global namespace to replace C's function global namespace. That is a huge improvement on its own. > It makes a large difference - both for code size and speed I only recently learned of how static impacts ELF visibility and therefore performance. Windows does not work this way, and I'm sure MacOS does either. Non-static on Windows does not imply replacable at dynamic link time, not even if the function is exported. Symbols are always resolved directly within the dll/sharedobject by the static linker if they are present with no pointer or stub in the way. (Ok, if you incorrectly annotate as __declspec(dllexport) and you don't use LTO/LTCG, then you will call through a pointer, but no stub, no actual inter-positionableness, and it is a rare occurence.) There is also always a two level namespace -- imported functions are qualified by the dll name they are expected to be in. For multiple dlls to export the same function name creates no ambiguity and implies no replacement of one by the other, and no semantic difference depending on load order. Unless someone writes very wierd code calling dlopen/dlsym like crazy. There is no LD_PRELOAD, slight loss, and replacing e.g. operator new isn't really possible process-wide, only within the scope of the static link, and even that is so rare, that it is probably sufficient. There is no going out of the way to accurately simulate the static linker at dynamic link time. Functions are only exported if they are annotated in source or listed in a separate file. Not just by being non-static. - Jay ________________________________ From: David Brown <da...@westcontrol.com> Sent: Monday, January 22, 2018 10:42 AM To: Jay K; gcc Subject: Re: extern const initialized warns in C On 22/01/2018 11:14, Jay K wrote: > I meant: > > > extern const foo = 123; > > > does not warn in C++, but by these arguments, should. > Yes, I think it should. But I am a compiler user, not a compiler author, so my bias is strongly towards /my/ code rather than a wider audience. > > > I understand that const int foo = 123 is static in C++. > > It is this difference between C and C++ and the desire to write code > that means the same in C and C++ is why I write extern const int foo = > 123 in the first place. If I was just writing C forever and not planning > to compile as C++ ever then I would omit the redundant extern -- and not > get a warning -- the other inconsistency! As I suggested, put the declaration in the header and the definition in the source file. Then it is the same code for C and C++, works correctly, and gives no warnings no matter what flags you use. And it is modular, structured, and lets programmers see exactly what is "exported" from that C file by looking in a short header rather than digging through a long source file. > > > To repeat for clarity: > > > 1 C: extern const int foo = 123; => warns, for reasons explained and > understood even if not agreed > > 2 C: const int foo = 123; => means the same thing, but no warning; > inconsistent? > > 3 C++: extern const int foo = 123; => also no warning, inconsistent? > > > > The prohibition against file level static is actually quite widespread > and adhered to. > Can you give references or links? As I say, I think such a convention is /seriously/ wrong. (There are plenty of other conventions that I think are wrong - even famous and "professional" standards like MISRA have some daft ideas.) > > Along with it, of course, comes a mandate to pick globally unique names. That mandate I can understand. There are rational justifications for it, even though I don't agree with them and think it is unworkable except for very small and limited programs. But there is nothing to stop you using "static" along with the globally unique names. > > Given a large C code base with many external identifiers, the extra step > of barring static and requiring incrementally more symbols to be unique > is not clearly a big deal. I already have to ensure uniqueness of many > symbols, what is a few more? > The majority of your file-level identifiers in a C file will normally /not/ be exported - they will only ever be used internally. I have no statistics, but I'd guess that under 10% of most C file's file-level identifiers are used externally in my "average" C file. That means only 10% need to be globally unique. > > > But I understand, perhaps in other systems, statics vastly outnumber > externs, and the condition would be a large increment not a small one. > Yes, exactly. Of course this sort of thing will vary somewhat depending on the type of coding you are doing. > > > While I struggle not bite my tongue advocating switching from C to C++, > this factor seems much less sigificant or harmful. I don't follow. If you want to switch from C to C++, that's fine by me :-) C++ gives you namespaces, which gives you another way to group your identifiers and control their scope. > > I have actually seen and heard of the accidental duplication of symbols > due to static multiple times. If you put your static variables in the C file, it is not "accidental" duplication - they are different objects. If you put static (non-const) variables in your header files, you have misunderstood how to use header files in C programming. > > I also do a "unity build" (one source file includes the rest) of my own > code to eek out a little extra performance (even while already using > LTO/LTCG) and had to work out the static duplication. > Using "static" does not hinder that. And it can be a significant benefit in performance. So far, we have been looking at this from the viewpoint of code correctness, easy of reading and writing the code, and avoiding subtle errors. But since you are interested in performance or code size, then you /really/ should be using "static" at every possible opportunity. It makes a large difference - both for code size and speed, when you have "static" on your variables and functions that don't have to be globally visible. > > > You mention the debugger ease of use. > > I haven't seen debuggers struggle with static but I have seen file > dumpers struggle (not objdump per se, but similar) > > I use the debugger as a better disassembler instead for this very > reason, debugger can just load up a file for examination without running > anything. > > > > And then, you should realize there are very large systems that output > nightly builds with symbols, taking hours on fast machines, where > individual developers only build their little part of the tree, but > debug anything. > I realise that. And "static" is your friend there too. I could not imagine trying to use a large build system or multiple developers where everything is global and shared, and you are trying to keep unique names everywhere. It is a recipe for pointlessly increasing build times, and massively increasing developer time. > > > In these systems, it behooves the mainline source to be debuggable, and > not rely on temporary edits and rebuilds. > > That is, if you would remove static for temporary debuggability, then it > follows you should remove it for long term debuggability. No, you would only remove "static" for temporary debugging of a local section of code - not for a big debugging session of such massive builds. It is just one of the many types of small temporary code changes that can be helpful during debugging, such as adding "volatile" or extra "printf". > > There is an amazing diversity of coding styles/idioms and C and C++ > compilers need to be ridiculously careful in imposing or suggesting > restrictions. I strive to be warning-free as well, perhaps untenable. Yes, there are lots of conventions and styles. And as I have said, I fully agree that there should be a flag for this particular issue. People have the right to make poor choices if they want (especially since these poor choices are usually made by a PHB or some ancient programmer who hasn't noticed that the world has changed during the last 20+ years and still thinks K&R is the defining word on C - and the unfortunate low-rank programmers just have to live with them). (Apologies if I sound too harsh in my opinions here - they come from being an ancient programmer who /has/ noticed how things have changed over the last 20+ years. But I work in a relatively narrow field - small systems embedded programming - and your experience may differ in other fields.) mvh., David > > > - Jay > > > > ------------------------------------------------------------------------ > *From:* David Brown <da...@westcontrol.com> > *Sent:* Monday, January 22, 2018 9:53 AM > *To:* Jay K; gcc > *Subject:* Re: extern const initialized warns in C > > > On 22/01/2018 10:31, Jay K wrote: >> >> By this argument there is a missing warning for the equivalent: >> >> const int foo = 123; >> >> with no previous extern declaration. > > I would like to see such a warning. There is "-Wmissing-declarations", > but that applies only to functions and not to objects. > > (Note that in C++, "const" objects without an "extern" declaration are > effectively "static" - in C, without the storage-class specifier const > objects have external linkage.) > >> >> As well, there is no warning in C++. >> All three constructs are equivalent, yet only one gets a warning. > > No, they are not the same - in C++ the linkage of a const is static > unless you explicitly say "extern", and in C it is external unless you > explicitly say "static". > > Thus in "extern const int foo = 123;", the "extern" has a significant > effect in C++ but in C it does nothing (other than inform the reader). > > I would like to see the warning having a controlling flag. It could > perhaps be on by default in C and off by default in C++ to get the same > effect as today - and then users can fine-tune to fit their style. > >> >> Interesting point, that I had not realized, and with an often acceptable >> workaround, however also there exist coding conventions that prohibit use of >> static. > > I have never heard of such a thing in a coding standard. C++ > conventions might encourage the use of anonymous namespaces rather than > C-style "static" declarations, but that would not apply to C. I would > consider a coding convention that discouraged static to be seriously broken. > >> Instead they "hide" things by omitting them from headers only. > > That is madness. The symbols still have global linkage across the > program, and you will get all sorts of problems when one file uses the > same "local" identifier as another. If you are lucky, your linker will > tell you of the crash - but if you have enabled "common" data (i.e., you > don't have the "-fno-common" flag) and have used the same identifier for > two different "local" objects without explicit initialisation, you are > going to have some serious and very hard to find bugs. > > If someone asks you to write to such a coding convention, do your best > to refuse. > >> >> That can still be worked around, just put the declaration right before the >> definition, >> in the same source file. >> >> I realize there are many arguments for and against file level static. >> > > There are no /good/ arguments against file-level static in C, except > perhaps temporarily while debugging (it can be easier to view non-static > data in a debugger). Any time file-level static can be used, it > /should/ be used. > > IMHO, of course. > > mvh., > > David > > >> - Jay >> >> >> From: David Brown <da...@westcontrol.com> >> Sent: Monday, January 22, 2018 8:32 AM >> To: Jay K; gcc >> Subject: Re: extern const initialized warns in C >> >> >> On 21/01/18 08:12, Jay K wrote: >>> extern const int foo = 123; >>> >>> >>> >>> Why does this warn? >>> This is a valid portable form, with the same meaning >>> across all compilers, and, importantly, portably >>> to C and C++. >>> >>> I explicitly do not want to say: >>> >>> const int foo = 123 >>> >>> because I want the code to be valid and have the same meaning >>> in C and C++ (modulo name mangling). >>> >>> I end up with: >>> >>> // Workaround gcc warning. >>> #ifdef __cplusplus >>> #define EXTERN_CONST extern const >>> #else >>> #define EXTERN_CONST const >>> #endif >>> >>> >>> EXTERN_CONST int foo = 123; >>> >>> and having to explain it to people. >>> >> >> <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=45977> 45977 – "warning: 'i' initialized and declared 'extern ...<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=45977> gcc.gnu.org GCC Bugzilla – Bug 45977 "warning: 'i' initialized and declared 'extern'" could use a separate warning flag controlling it Last modified: 2017-07-26 15:36:22 UTC > 45977 – "warning: 'i' initialized and declared 'extern ... > <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=45977> 45977 – "warning: 'i' initialized and declared 'extern ...<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=45977> gcc.gnu.org GCC Bugzilla – Bug 45977 "warning: 'i' initialized and declared 'extern'" could use a separate warning flag controlling it Last modified: 2017-07-26 15:36:22 UTC > gcc.gnu.org > GCC Bugzilla – Bug 45977 "warning: 'i' initialized and declared > 'extern'" could use a separate warning flag controlling it Last > modified: 2017-07-26 15:36:22 UTC > > >> >> >> 45977 – "warning: 'i' initialized and declared 'extern ... >> gcc.gnu.org >> GCC Bugzilla – Bug 45977 "warning: 'i' initialized and declared 'extern'" >> could use a separate warning flag controlling it Last modified: 2017-07-26 >> 15:36:22 UTC >> >> This suggests that gcc authors consider mixing "extern" and >> initialization to be such bad style that the compiler warns by default. >> But the "bug" is that there is no flag to turn off this warning. >> (Ideally every warning should have a matching flag, even if the warning >> is enabled by default.) >> >> Usually you do not want to have "extern" and initialisation in the same >> line - it indicates a questionable organisation of your sources which is >> more likely to be error-prone than the standard idioms. (I say >> "questionable", not necessarily wrong - but certainly I would question >> it if I saw it in source code.) >> >> Normally you want: >> >> // file.h >> // declaration, not definition >> extern const int foo; >> >> // file.c >> #include <file.h> >> // definition >> const int foo = 123; >> >> // otherfile.c >> #include <file.h> >> int usefoo(void) { return foo; } >> >> >> The key advantages of this sort of setup are a cleaner separation >> between declarations (which you need to /use/ things) and the >> definitions (which should normally only exist once in the program - >> certainly for C). The declarations and definitions only exist in one >> place, and they are checked for consistency - there are no "extern" >> declarations lying around in C files that might get out of step from >> changes in the headers or other files with definitions. >> >> To be consistent with this, and to work consistently with C and C++, I >> have a strict policy that a C (or C++) file never contains declarations >> without definitions (and initialisations as needed), with each >> definition either also declared as "extern" in a matching header file, >> or it is declared as "static". >> >> This sort of arrangement is very common - though many people are lazy >> about using "static". (In C++, you can also use anonymous namespaces, >> but "static" works for consistency between C and C++.) >> >> >> Still, gcc should have a flag to disable this warning if you have reason >> to use "extern const int foo = 123;" - it is, after all, correctly >> defined C code. >> >> >> >>> $ cat 1.c >>> extern const int foo = 123; >>> $ $HOME/gcc720/bin/gcc -c -S 1.c >>> 1.c:1:18: warning: 'foo' initialized and declared 'extern' >>> extern const int foo = 123; >>> ^~~ >>> $ $HOME/gcc720/bin/gcc -c -S -xc++ -Wall -pedantic 1$ $HOME/gcc720/bin/gcc >>> -v >>> Using built-in specs. >>> >>> COLLECT_GCC=/Users/jay/gcc720/bin/gcc >>> COLLECT_LTO_WRAPPER=/Users/jay/gcc720/libexec/gcc/x86_64-apple-darwin16.7.0/7.2.0/lto-wrapper >>> Target: x86_64-apple-darwin16.7.0 >>> Configured with: ../gcc-7.2.0/configure -prefix=/Users/jay/gcc720 >>> -disable-nls -disable-bootstrap >>> Thread model: posix >>> gcc version 7.2.0 (GCC) $ >>> >>> >>> Thank you, >>> - Jay >>> >>> >>> >>> >>> >> >> >>