Hi,

I think we are getting quite a bit off-topic for the gcc list here. We should probably take the discussion to somewhere like comp.lang.c. So I shall limit things to just a couple of points to round off.

On 22/01/2018 12:27, Jay K wrote:
  > 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;



If they get allocated memory, then this will be duplicated across all units that have the same const (or static const for C). But usually small constants do not get put in memory - they get used directly, and the opportunities for constant propagation optimisations typically outweigh the occasional duplication of memory. Even taking the address of a const does not force it to have a memory allocation (gcc is smart here), and if you want you can use "-fmerge-all-constants" to save space on duplicate constants, at the cost of possibly causing trouble for code that expects separate constants to have separate addresses.

  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.

We are taking about wildly different kinds of debugging here.


  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.


There is no pretence needed - there /is/ the same global namespace. Named and anonymous namespaces are an addition, not a replacement.


  > It makes a large difference - both for code size and speed

  I only recently learned of how static impacts ELF visibility
 and therefore performance.

Elf visibility is a different thing. Symbols can have external linkage for C but hidden visibility in elf. Elf symbol visibility affects the time taken for linking, and only affects runtime performance in terms of dynamic linking time (time to start the executable and to link dynamic libraries). It is something you want to handle carefully if you have large C++ libraries where you can easily end up with vast numbers of symbols.

Windows does not work this way,

Windows uses COFF format, not ELF.

  and I'm sure MacOS does either.

MacOS is BSD underneath, and uses ELF.

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

None of this has anything to do with "static" and the significantly improved optimisations it allows.

Basically, if an object or function is static and does not "escape" (by passing its address out of the translation unit, or similar things), then the compiler knows all the uses of that object. It can inline the function without making extra copies, it can remove some reads or writes to the variable or move them across function calls, it can omit static objects and functions that are not used, it can pre-compute results based on static consts because it knows they can never change. Declaring things "static" opens up a wide range of optimisations, many of which have knock-on effects allowing more optimisations.

If you are interested in code efficiency, and you are not using "static" whenever possible, you are hobbling your compiler.

mvh.,

David



 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




Reply via email to