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 ...
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




Reply via email to