• Global const objects missing from object file

    From Steve Keller@21:1/5 to All on Fri Oct 8 22:47:28 2021
    I am surprised I cannot define a global const object in one file and
    access it in another:

    --------- x.cc ---------
    const int a = 42
    ------------------------

    --------- y.cc ---------
    extern const int a;

    int foo() { return a; }
    ------------------------

    When I compile both sources to objects I see access to 'a' as
    expected:

    movl a(%rip), %eax
    ret

    But the object file x.o does not contain anything. Why?

    If I remove the 'const' in the definition and in the extern
    declaration it works as expected. Also if add an 'extern' to the
    definition in x.cc so that it is

    extern const int a = 42;

    the object 'a' is created in x.o. Can someone explain this?

    Steve

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From red floyd@21:1/5 to Steve Keller on Fri Oct 8 15:09:42 2021
    On 10/8/2021 1:47 PM, Steve Keller wrote:
    I am surprised I cannot define a global const object in one file and
    access it in another:

    --------- x.cc ---------
    const int a = 42
    ------------------------

    --------- y.cc ---------
    extern const int a;

    int foo() { return a; }
    ------------------------

    When I compile both sources to objects I see access to 'a' as
    expected:

    movl a(%rip), %eax
    ret

    But the object file x.o does not contain anything. Why?

    If I remove the 'const' in the definition and in the extern
    declaration it works as expected. Also if add an 'extern' to the
    definition in x.cc so that it is

    extern const int a = 42;

    the object 'a' is created in x.o. Can someone explain this?

    Const objects have internal linkage unless it is specified with
    "extern". I think it's in 6.2.1 in C++17, but I won't swear to it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paavo Helde@21:1/5 to All on Sat Oct 9 10:47:04 2021
    08.10.2021 23:47 Steve Keller kirjutas:
    I am surprised I cannot define a global const object in one file and
    access it in another:

    --------- x.cc ---------
    const int a = 42
    ------------------------

    --------- y.cc ---------
    extern const int a;

    int foo() { return a; }
    ------------------------

    When I compile both sources to objects I see access to 'a' as
    expected:

    movl a(%rip), %eax
    ret

    But the object file x.o does not contain anything. Why?

    If I remove the 'const' in the definition and in the extern
    declaration it works as expected. Also if add an 'extern' to the
    definition in x.cc so that it is

    extern const int a = 42;

    the object 'a' is created in x.o. Can someone explain this?


    Yes, this is working as designed, in C++. I believe this is one of
    differences between C and C++.

    Such global const definitions normally appear in a common header file
    and would just cause duplicate linker symbols if they had external
    linkage. Now they can be just optimized away in translation units which
    do not use them.

    I believe the original motivation was to have an easy replacement of C
    macros in header files, i.e. instead of

    #define a 42

    one can easily use

    const int a = 42;

    which would function almost exactly like a #define, plus it honors C++ namespaces and cannot be undefined.

    They messed it up though with class static const members, which by some
    reason did not follow the same rules and caused a lot of randomly
    appearing obscure linker errors.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Alf P. Steinbach@21:1/5 to Steve Keller on Sat Oct 9 15:05:10 2021
    On 8 Oct 2021 22:47, Steve Keller wrote:
    I am surprised I cannot define a global const object in one file and
    access it in another:

    --------- x.cc ---------
    const int a = 42

    Assuming the code actually has a semicolon here, so that it compiles.
    Copy and paste code to avoid such typos.


    ------------------------

    --------- y.cc ---------
    extern const int a;

    int foo() { return a; }
    ------------------------

    When I compile both sources to objects I see access to 'a' as
    expected:

    movl a(%rip), %eax
    ret

    But the object file x.o does not contain anything. Why?

    Because without `extern` the definition in `x.cpp` has internal linkage,
    as noted by Red Floyd in his response.

    And since it's not visible outside the translation unit, and is not used
    within it, and its initialization has no side effects, it's optimized away.


    If I remove the 'const' in the definition and in the extern
    declaration it works as expected. Also if add an 'extern' to the
    definition in x.cc so that it is

    extern const int a = 42;

    the object 'a' is created in x.o. Can someone explain this?

    See above.

    A good way to avoid these problems is to place the declaration of `a`,
    with an `extern`, in a header file that you include both in the defining
    source file and in the using source file.

    Because, when the compiler has seen the `extern`-ness of `a` once in a translation unit, then in any subsequent encounter of `a` that
    `extern`-ness is implied.


    - Alf

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Paavo Helde on Sat Oct 9 14:32:13 2021
    Paavo Helde <myfirstname@osa.pri.ee> writes:
    08.10.2021 23:47 Steve Keller kirjutas:
    I am surprised I cannot define a global const object in one file and
    access it in another:


    They messed it up though with class static const members, which by some >reason did not follow the same rules and caused a lot of randomly
    appearing obscure linker errors.

    I would not characterize the linker errors as either random or obscure.

    Annoying, yes. But the solution is simple, if not necessarily pretty.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juha Nieminen@21:1/5 to Steve Keller on Sat Oct 9 17:40:11 2021
    Steve Keller <keller.steve@gmx.de> wrote:
    If I remove the 'const' in the definition and in the extern
    declaration it works as expected. Also if add an 'extern' to the
    definition in x.cc so that it is

    extern const int a = 42;

    the object 'a' is created in x.o. Can someone explain this?

    This is one key difference between C and C++.

    In C, const variables at the global namespace level have external linkage
    by default. In other words, they are implicitly 'extern'.

    In C++, const variables at the namespace level (including the global
    namespace) have internal linkage by default. In other words, they are implicitly 'static'.

    In C, if you want a const variable in the global namespace to have
    internal linkage, you need to explicitly specify 'static' in its
    declaration.

    In C++, if you want a const variable at a namespace level (including
    the global namespace) to have external linkage, you need to explicitly
    specify 'extern' in its declaration.

    (In C++ this is true for *any* type, not just basic types.)

    Previously this was a minor nuisance in C++ because a 'const' variable
    in a header would be duplicated in each object file that used it, if
    the compiler couldn't optimize it away (ie. "inline" it). If you wanted
    only one instance of that const variable, you had to 'extern' it manually.

    C++17 added support for "inline const" variables, which does this for
    you automatically (in the same way as 'inline' does with functions).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paavo Helde@21:1/5 to All on Sat Oct 9 21:39:57 2021
    09.10.2021 17:32 Scott Lurndal kirjutas:
    Paavo Helde <myfirstname@osa.pri.ee> writes:
    08.10.2021 23:47 Steve Keller kirjutas:
    I am surprised I cannot define a global const object in one file and
    access it in another:


    They messed it up though with class static const members, which by some
    reason did not follow the same rules and caused a lot of randomly
    appearing obscure linker errors.

    I would not characterize the linker errors as either random or obscure.

    If a linker errors start to appear after some straightforward code
    rewrite like replacing an if() with the ternary operator, and only with
    some compilers and only with some optimization options, surely they will
    look random and obscure if you have not encountered such issues before.

    Yes, they are not obscure once you already understand the issue.

    Annoying, yes. But the solution is simple, if not necessarily pretty.

    IIRC at one point one had to use different solutions for different
    mainstream compilers, as there was no code variant accepted by both. But
    that was long ago.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Steve Keller@21:1/5 to Paavo Helde on Sun Oct 10 08:55:41 2021
    Paavo Helde <myfirstname@osa.pri.ee> writes:

    Such global const definitions normally appear in a common header file
    and would just cause duplicate linker symbols if they had external
    linkage. Now they can be just optimized away in translation units
    which do not use them.

    I believe the original motivation was to have an easy replacement of C
    macros in header files, i.e. instead of

    #define a 42

    one can easily use

    const int a = 42;

    which would function almost exactly like a #define, plus it honors C++ namespaces and cannot be undefined.

    If you write such "global" const objects into a header file they are
    in fact not global, because the implicit 'static' makes them local to
    file translation unit and this can even cause duplication:

    $ cat foo.hh
    const int a = 42;

    const int *bar();
    $ cat x.cc
    #include "foo.hh"

    const int *bar() { return &a; }
    $ cat y.cc
    #include <iostream>

    #include "foo.hh"

    const int *foo() { return &a; }

    int main()
    {
    std::cout << (void *)foo() << '\n' << (void *)bar() << '\n';
    }
    $ g++ -Os -o foo x.cc y.cc
    $ ./foo
    0x56027d731008
    0x56027d731004

    For a small 'int' one may not care but for larger constant objects
    this may be really bad.

    To avoid it, I think you still have to write the defintion into only
    one C++ source file using 'extern'

    extern const int = 42;

    and put

    extern const int a;

    into the header file. I still don't see the advantage for C++ to
    differ from C here.


    Steve

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Steve Keller@21:1/5 to red floyd on Sun Oct 10 08:28:51 2021
    red floyd <no.spam.here@its.invalid> writes:

    Const objects have internal linkage unless it is specified with
    "extern". I think it's in 6.2.1 in C++17, but I won't swear to it.

    I thought I havd already used this like in C and it worked. But I'm
    obviously wrong since I tested with very old GNU C++ compiler and it
    also shows this behavior.

    But I wonder why C++ differs here from C when C already has 'static'
    for this purpose. And why treat const and non-const so differently?
    It means you have to use 'static' to make a non-const object internal
    and 'extern' to make a const object external.

    Steve

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paavo Helde@21:1/5 to All on Sun Oct 10 12:14:44 2021
    10.10.2021 09:55 Steve Keller kirjutas:
    Paavo Helde <myfirstname@osa.pri.ee> writes:

    Such global const definitions normally appear in a common header file
    and would just cause duplicate linker symbols if they had external
    linkage. Now they can be just optimized away in translation units
    which do not use them.

    I believe the original motivation was to have an easy replacement of C
    macros in header files, i.e. instead of

    #define a 42

    one can easily use

    const int a = 42;

    which would function almost exactly like a #define, plus it honors C++
    namespaces and cannot be undefined.

    If you write such "global" const objects into a header file they are
    in fact not global, because the implicit 'static' makes them local to
    file translation unit and this can even cause duplication:

    $ cat foo.hh
    const int a = 42;

    const int *bar();
    $ cat x.cc
    #include "foo.hh"

    const int *bar() { return &a; }
    $ cat y.cc
    #include <iostream>

    #include "foo.hh"

    const int *foo() { return &a; }

    int main()
    {
    std::cout << (void *)foo() << '\n' << (void *)bar() << '\n';
    }
    $ g++ -Os -o foo x.cc y.cc
    $ ./foo
    0x56027d731008
    0x56027d731004

    For a small 'int' one may not care but for larger constant objects
    this may be really bad.

    The unused const objects are routinely optimized away be the compiler,
    as you saw by yourself ("object file was empty").

    It's true that for larger and more complicated objects this might not
    work so well. So don't do that. Put the object in a single TU and
    provide functions to access it. (Making the object extern is not safe in general because of the "Static Initialization Order Fiasco".)


    To avoid it, I think you still have to write the defintion into only
    one C++ source file using 'extern'

    extern const int = 42;

    and put

    extern const int a;

    into the header file. I still don't see the advantage for C++ to
    differ from C here.

    In some sense the do not differ. The const globals in C++ are primarily
    meant for replacing C #define. Each C #define is TU-specific and gets
    fully preprocessed by the preprocessor, no other TU-s are involved.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paavo Helde@21:1/5 to All on Sun Oct 10 12:03:56 2021
    10.10.2021 09:28 Steve Keller kirjutas:
    red floyd <no.spam.here@its.invalid> writes:

    Const objects have internal linkage unless it is specified with
    "extern". I think it's in 6.2.1 in C++17, but I won't swear to it.

    I thought I havd already used this like in C and it worked. But I'm obviously wrong since I tested with very old GNU C++ compiler and it
    also shows this behavior.

    But I wonder why C++ differs here from C when C already has 'static'
    for this purpose. And why treat const and non-const so differently?
    It means you have to use 'static' to make a non-const object internal
    and 'extern' to make a const object external.

    Global objects have pretty limited use in C++, because of the "Static Initialization Order Fiasco". As soon as one such global object in one translation unit starts to depend on another global object in another translation unit, you cannot be sure any more they are constructed in
    the right order.

    In C++ this is a bigger problem than in C as C++ relies heavily on
    constructors whose code would run before main() for global objects.

    In practice, one typically hides global non-const objects away as a
    static object inside some function, or at least accessed only via
    functions in a single TU, which makes the object creation order much
    better controlled.

    Const global objects are TU-specific by default, so the problem does not
    occur. As soon as you make them extern, the danger of the initialization
    fiasco appears, even for something so simple as an int.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bo Persson@21:1/5 to Steve Keller on Sun Oct 10 10:34:08 2021
    On 2021-10-10 at 08:55, Steve Keller wrote:
    Paavo Helde <myfirstname@osa.pri.ee> writes:

    Such global const definitions normally appear in a common header file
    and would just cause duplicate linker symbols if they had external
    linkage. Now they can be just optimized away in translation units
    which do not use them.

    I believe the original motivation was to have an easy replacement of C
    macros in header files, i.e. instead of

    #define a 42

    one can easily use

    const int a = 42;

    which would function almost exactly like a #define, plus it honors C++
    namespaces and cannot be undefined.

    If you write such "global" const objects into a header file they are
    in fact not global, because the implicit 'static' makes them local to
    file translation unit and this can even cause duplication:

    $ cat foo.hh
    const int a = 42;

    const int *bar();
    $ cat x.cc
    #include "foo.hh"

    const int *bar() { return &a; }
    $ cat y.cc
    #include <iostream>

    #include "foo.hh"

    const int *foo() { return &a; }

    int main()
    {
    std::cout << (void *)foo() << '\n' << (void *)bar() << '\n';
    }
    $ g++ -Os -o foo x.cc y.cc
    $ ./foo
    0x56027d731008
    0x56027d731004

    For a small 'int' one may not care but for larger constant objects
    this may be really bad.


    It only causes duplication if you take the address and return a pointer.
    Why do that for a constant defined in a header?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Steve Keller on Sun Oct 10 12:29:13 2021
    On 10/10/2021 08:28, Steve Keller wrote:
    red floyd <no.spam.here@its.invalid> writes:

    Const objects have internal linkage unless it is specified with
    "extern". I think it's in 6.2.1 in C++17, but I won't swear to it.

    I thought I havd already used this like in C and it worked. But I'm obviously wrong since I tested with very old GNU C++ compiler and it
    also shows this behavior.

    But I wonder why C++ differs here from C when C already has 'static'
    for this purpose. And why treat const and non-const so differently?
    It means you have to use 'static' to make a non-const object internal
    and 'extern' to make a const object external.

    Steve


    I think this goes back to early history (and I hope and expect to be
    corrected if I've got it wrong). "const" was first part of C++, before
    being copied over to C. So when it was introduced in C++ there was no conflicting version in C. Since one of the ideas behind it was to get a
    better alternative to #define constants, it was important to be
    convenient in headers, and that for common usage such as integer
    constants, it could be as efficient - there was no need to make the
    constant an actual allocated object in memory. Add to that, the idea of objects and identifiers being of external linkage by default is a
    /major/ design mistake in C (IMHO, of course). It is far better to have
    things with internal linkage unless you /really/ want to make them
    available in other units. Perhaps making "const" objects "static" by
    default was seen as a step to correct this.

    So the question is why C didn't copy that aspect when they copied
    "const". I guess they thought it was simpler and more consistent to
    have the same rules for const objects as for non-const objects.

    In headers, I tend to be explicit about using "static" or "extern", even
    when it might not be necessary - I prefer explicit and it's not uncommon
    for a header to be usable from both C and C++.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Steve Keller on Sun Oct 10 12:33:51 2021
    On 10/10/2021 08:55, Steve Keller wrote:
    Paavo Helde <myfirstname@osa.pri.ee> writes:

    Such global const definitions normally appear in a common header file
    and would just cause duplicate linker symbols if they had external
    linkage. Now they can be just optimized away in translation units
    which do not use them.

    I believe the original motivation was to have an easy replacement of C
    macros in header files, i.e. instead of

    #define a 42

    one can easily use

    const int a = 42;

    which would function almost exactly like a #define, plus it honors C++
    namespaces and cannot be undefined.

    If you write such "global" const objects into a header file they are
    in fact not global, because the implicit 'static' makes them local to
    file translation unit and this can even cause duplication:

    $ cat foo.hh
    const int a = 42;

    const int *bar();
    $ cat x.cc
    #include "foo.hh"

    const int *bar() { return &a; }
    $ cat y.cc
    #include <iostream>

    #include "foo.hh"

    const int *foo() { return &a; }

    int main()
    {
    std::cout << (void *)foo() << '\n' << (void *)bar() << '\n';
    }
    $ g++ -Os -o foo x.cc y.cc
    $ ./foo
    0x56027d731008
    0x56027d731004

    For a small 'int' one may not care but for larger constant objects
    this may be really bad.

    To avoid it, I think you still have to write the defintion into only
    one C++ source file using 'extern'

    extern const int = 42;

    You don't (IMHO) want "extern" here - you want to make sure that this
    file includes the header with the "extern const int a;" declaration.
    (And you forgot the "a" in that line.)


    and put

    extern const int a;

    into the header file. I still don't see the advantage for C++ to
    differ from C here.


    It is convenient when you have const objects defined within a file and
    that are local to the file - there is no need for a "static". (A lot of
    people are far too lazy about putting "static" where it is appropriate
    in their C and C++ coding.)

    With C++17, you can also write:

    extern inline const int a = 42;

    in the header, and you don't need a definition in a C++ file.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Steve Keller@21:1/5 to Bo Persson on Sun Oct 10 15:55:36 2021
    Bo Persson <bo@bo-persson.se> writes:

    It only causes duplication if you take the address and return a
    pointer. Why do that for a constant defined in a header?

    But taking the address is very common, e.g. if the object is an array.
    In fact, before I posted my first example with 'const int a = 42;' I
    had observed the behavior with const char arrays. Here You see the
    duplication if you put it as non-extern const into a header file:

    $ cat foo.hh
    const char s[] = "This is a string";

    void bar();
    $ cat x.cc
    #include <iostream>

    #include "foo.hh"

    void bar() { std::cout << s; }
    $ cat y.cc
    #include <iostream>

    #include "foo.hh"

    int main()
    {
    bar();
    std::cout << s;
    }
    $ g++ -Os -o foo x.cc y.cc
    $ strings -a foo | grep This
    This is a string
    This is a string

    So, I still don't see the purpose of defining const global objects as
    internal linkage implicitly so you can put them into a header file.
    If I'd want that I could also put a 'static' qualifier. But usually
    you'd wouldn't want that anyway except for basic types and instead
    define it once in one TU and declare it as external in the hear file
    (if it's simple enough that initialization order doesn't matter). And
    then we would have more compatability to C and less surprise for C
    programmers (which I have been for a long time).

    Steve

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paavo Helde@21:1/5 to All on Sun Oct 10 17:28:51 2021
    10.10.2021 16:55 Steve Keller kirjutas:
    Bo Persson <bo@bo-persson.se> writes:

    It only causes duplication if you take the address and return a
    pointer. Why do that for a constant defined in a header?

    But taking the address is very common, e.g. if the object is an array.
    In fact, before I posted my first example with 'const int a = 42;' I
    had observed the behavior with const char arrays. Here You see the duplication if you put it as non-extern const into a header file:

    $ cat foo.hh
    const char s[] = "This is a string";

    void bar();
    $ cat x.cc
    #include <iostream>

    #include "foo.hh"

    void bar() { std::cout << s; }
    $ cat y.cc
    #include <iostream>

    #include "foo.hh"

    int main()
    {
    bar();
    std::cout << s;
    }
    $ g++ -Os -o foo x.cc y.cc
    $ strings -a foo | grep This
    This is a string
    This is a string

    For having only one string the duplicates need to be removed at link
    time, so one needs -flto here. Also you should avoid to explicitly
    define two different arrays as indeed these would need to be different
    objects.

    With the following changes I get one string only in the final executable:

    $ cat foo.hh
    const char* const s = "This is a string";
    void bar();

    $ g++ -Os -o foo x.cc y.cc -flto

    $ strings foo | grep 'This is'
    This is a string

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Steve Keller@21:1/5 to Paavo Helde on Sun Oct 10 20:42:22 2021
    Paavo Helde <myfirstname@osa.pri.ee> writes:

    For having only one string the duplicates need to be removed at link
    time, so one needs -flto here. Also you should avoid to explicitly
    define two different arrays as indeed these would need to be different objects.

    -flto doesn't help with the source that I have shown.

    With the following changes I get one string only in the final executable:

    $ cat foo.hh
    const char* const s = "This is a string";
    void bar();

    $ g++ -Os -o foo x.cc y.cc -flto

    $ strings foo | grep 'This is'
    This is a string

    Yes, but then you add a const pointer variable in each TU and an
    indirection with every access to the const object (it also probably
    adds a number of entries to the relocation table of the executable
    binary slowing startup). And I wouldn't like the superfluous
    indirection when the object is at a fixed known address. Defining
    only one object of type

    const char[]

    with external linkage is preferable IMHO. Having read in this thread
    that 'const' probably originated in C++ and the designers felt that
    default external linkage was wrong in the beginning, I begin to
    understand the reasoning behind the C++ const behavior. Whether I can
    make myself to like it, ... I don't know.

    Steve

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paavo Helde@21:1/5 to All on Sun Oct 10 22:44:56 2021
    10.10.2021 21:42 Steve Keller kirjutas:
    Paavo Helde <myfirstname@osa.pri.ee> writes:

    For having only one string the duplicates need to be removed at link
    time, so one needs -flto here. Also you should avoid to explicitly
    define two different arrays as indeed these would need to be different
    objects.

    -flto doesn't help with the source that I have shown.

    With the following changes I get one string only in the final executable:

    $ cat foo.hh
    const char* const s = "This is a string";
    void bar();

    $ g++ -Os -o foo x.cc y.cc -flto

    $ strings foo | grep 'This is'
    This is a string

    Yes, but then you add a const pointer variable in each TU

    In each TU which uses it, most probably, and even then it has good
    chances to be completely optimized away.

    And we need to put this thing into context. For example, a set of CUDA
    runtime libraries is 960 MB. A pointer of 8 bytes, duplicated in 10 TU-s
    which make use of it would consume 80 bytes. This is 0.000008% of CUDA libraries. Nobody will care about such things nowadays (except a certain
    one frequent poster in this group, and this is not me).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Paavo Helde on Fri Oct 29 06:29:35 2021
    Paavo Helde <myfirstname@osa.pri.ee> writes:

    10.10.2021 09:55 Steve Keller kirjutas:

    Paavo Helde <myfirstname@osa.pri.ee> writes:

    Such global const definitions normally appear in a common header file
    and would just cause duplicate linker symbols if they had external
    linkage. Now they can be just optimized away in translation units
    which do not use them.

    I believe the original motivation was to have an easy replacement of C
    macros in header files, i.e. instead of

    #define a 42

    one can easily use

    const int a = 42;

    which would function almost exactly like a #define, plus it honors C++
    namespaces and cannot be undefined.

    If you write such "global" const objects into a header file they are
    in fact not global, because the implicit 'static' makes them local to
    file translation unit and this can even cause duplication:

    $ cat foo.hh
    const int a = 42;
    const int *bar();
    $ cat x.cc
    #include "foo.hh"
    const int *bar() { return &a; }
    $ cat y.cc
    #include <iostream>
    #include "foo.hh"
    const int *foo() { return &a; }
    int main()
    {
    std::cout << (void *)foo() << '\n' << (void *)bar() << '\n';
    }
    $ g++ -Os -o foo x.cc y.cc
    $ ./foo
    0x56027d731008
    0x56027d731004

    For a small 'int' one may not care but for larger constant objects
    this may be really bad.

    The unused const objects are routinely optimized away be the compiler,
    as you saw by yourself ("object file was empty").

    It's true that for larger and more complicated objects this might not
    work so well. So don't do that. Put the object in a single TU and
    provide functions to access it. (Making the object extern is not safe
    in general because of the "Static Initialization Order Fiasco".)

    To avoid it, I think you still have to write the defintion into only
    one C++ source file using 'extern'

    extern const int = 42;

    and put

    extern const int a;

    into the header file. I still don't see the advantage for C++ to
    differ from C here.

    In some sense the do not differ. The const globals in C++ are
    primarily meant for replacing C #define. Each C #define is TU-specific
    and gets fully preprocessed by the preprocessor, no other TU-s are
    involved.

    It appears you have sidestepped the central question here. What a
    definition like 'const int foo = 7;' (without any mention anywhere
    of 'extern') does in C++ is, AFAICT, exactly the same as a similar
    definition with static, namely 'static const int foo = 7;'. If
    these two definitions are exactly the same (and I believe they are),
    why change the meaning of the version that doesn't say 'static'?
    Why introduce what appears to be a gratuitous incompatibility with C
    when there was already a perfectly good construct that could be used
    (and works in C++ now) for defining a local constant?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bo Persson@21:1/5 to Tim Rentsch on Fri Oct 29 15:55:29 2021
    On 2021-10-29 at 15:29, Tim Rentsch wrote:
    Paavo Helde <myfirstname@osa.pri.ee> writes:

    10.10.2021 09:55 Steve Keller kirjutas:

    Paavo Helde <myfirstname@osa.pri.ee> writes:

    Such global const definitions normally appear in a common header file
    and would just cause duplicate linker symbols if they had external
    linkage. Now they can be just optimized away in translation units
    which do not use them.

    I believe the original motivation was to have an easy replacement of C >>>> macros in header files, i.e. instead of

    #define a 42

    one can easily use

    const int a = 42;

    which would function almost exactly like a #define, plus it honors C++ >>>> namespaces and cannot be undefined.

    If you write such "global" const objects into a header file they are
    in fact not global, because the implicit 'static' makes them local to
    file translation unit and this can even cause duplication:

    $ cat foo.hh
    const int a = 42;
    const int *bar();
    $ cat x.cc
    #include "foo.hh"
    const int *bar() { return &a; }
    $ cat y.cc
    #include <iostream>
    #include "foo.hh"
    const int *foo() { return &a; }
    int main()
    {
    std::cout << (void *)foo() << '\n' << (void *)bar() << '\n';
    }
    $ g++ -Os -o foo x.cc y.cc
    $ ./foo
    0x56027d731008
    0x56027d731004

    For a small 'int' one may not care but for larger constant objects
    this may be really bad.

    The unused const objects are routinely optimized away be the compiler,
    as you saw by yourself ("object file was empty").

    It's true that for larger and more complicated objects this might not
    work so well. So don't do that. Put the object in a single TU and
    provide functions to access it. (Making the object extern is not safe
    in general because of the "Static Initialization Order Fiasco".)

    To avoid it, I think you still have to write the defintion into only
    one C++ source file using 'extern'

    extern const int = 42;

    and put

    extern const int a;

    into the header file. I still don't see the advantage for C++ to
    differ from C here.

    In some sense the do not differ. The const globals in C++ are
    primarily meant for replacing C #define. Each C #define is TU-specific
    and gets fully preprocessed by the preprocessor, no other TU-s are
    involved.

    It appears you have sidestepped the central question here. What a
    definition like 'const int foo = 7;' (without any mention anywhere
    of 'extern') does in C++ is, AFAICT, exactly the same as a similar
    definition with static, namely 'static const int foo = 7;'. If
    these two definitions are exactly the same (and I believe they are),
    why change the meaning of the version that doesn't say 'static'?
    Why introduce what appears to be a gratuitous incompatibility with C
    when there was already a perfectly good construct that could be used
    (and works in C++ now) for defining a local constant?


    A problem here is that C++ introduced the const keyword first.

    When C got it later, the C committee decided to do it slighly differently.

    Now what?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Tim Rentsch on Fri Oct 29 14:44:28 2021
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    Paavo Helde <myfirstname@osa.pri.ee> writes:

    In some sense the do not differ. The const globals in C++ are
    primarily meant for replacing C #define. Each C #define is TU-specific
    and gets fully preprocessed by the preprocessor, no other TU-s are
    involved.

    It appears you have sidestepped the central question here. What a
    definition like 'const int foo = 7;' (without any mention anywhere
    of 'extern') does in C++ is, AFAICT, exactly the same as a similar
    definition with static, namely 'static const int foo = 7;'.

    Is that not dependent upon scope? For example:

    class x {
    static const unsigned long FRED = 0xabcdef00ul;

    };

    requires an additional declaration in a compilation unit, e.g.

    const unsigned long x::FRED;


    While

    namespace y {
    const unsigned long FRED = 0xabcdef00ul;
    };

    doesn't require an additional declaration.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?B?w5bDtiBUaWli?=@21:1/5 to Bo Persson on Fri Oct 29 22:20:06 2021
    On Friday, 29 October 2021 at 16:55:47 UTC+3, Bo Persson wrote:
    On 2021-10-29 at 15:29, Tim Rentsch wrote:
    Paavo Helde <myfir...@osa.pri.ee> writes:

    10.10.2021 09:55 Steve Keller kirjutas:

    Paavo Helde <myfir...@osa.pri.ee> writes:

    Such global const definitions normally appear in a common header file >>>> and would just cause duplicate linker symbols if they had external
    linkage. Now they can be just optimized away in translation units
    which do not use them.

    I believe the original motivation was to have an easy replacement of C >>>> macros in header files, i.e. instead of

    #define a 42

    one can easily use

    const int a = 42;

    which would function almost exactly like a #define, plus it honors C++ >>>> namespaces and cannot be undefined.

    If you write such "global" const objects into a header file they are
    in fact not global, because the implicit 'static' makes them local to
    file translation unit and this can even cause duplication:

    $ cat foo.hh
    const int a = 42;
    const int *bar();
    $ cat x.cc
    #include "foo.hh"
    const int *bar() { return &a; }
    $ cat y.cc
    #include <iostream>
    #include "foo.hh"
    const int *foo() { return &a; }
    int main()
    {
    std::cout << (void *)foo() << '\n' << (void *)bar() << '\n';
    }
    $ g++ -Os -o foo x.cc y.cc
    $ ./foo
    0x56027d731008
    0x56027d731004

    For a small 'int' one may not care but for larger constant objects
    this may be really bad.

    The unused const objects are routinely optimized away be the compiler,
    as you saw by yourself ("object file was empty").

    It's true that for larger and more complicated objects this might not
    work so well. So don't do that. Put the object in a single TU and
    provide functions to access it. (Making the object extern is not safe
    in general because of the "Static Initialization Order Fiasco".)

    To avoid it, I think you still have to write the defintion into only
    one C++ source file using 'extern'

    extern const int = 42;

    and put

    extern const int a;

    into the header file. I still don't see the advantage for C++ to
    differ from C here.

    In some sense the do not differ. The const globals in C++ are
    primarily meant for replacing C #define. Each C #define is TU-specific
    and gets fully preprocessed by the preprocessor, no other TU-s are
    involved.

    It appears you have sidestepped the central question here. What a definition like 'const int foo = 7;' (without any mention anywhere
    of 'extern') does in C++ is, AFAICT, exactly the same as a similar definition with static, namely 'static const int foo = 7;'. If
    these two definitions are exactly the same (and I believe they are),
    why change the meaning of the version that doesn't say 'static'?
    Why introduce what appears to be a gratuitous incompatibility with C
    when there was already a perfectly good construct that could be used
    (and works in C++ now) for defining a local constant?

    A problem here is that C++ introduced the const keyword first.

    When C got it later, the C committee decided to do it slighly differently.

    Now what?

    Now we have to live with it. C+11 tried to add some clarity with constexpr
    and C++17 to turn it all into mess with (sometimes also implicit) inline.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Bo Persson on Sun Nov 7 04:51:50 2021
    Bo Persson <bo@bo-persson.se> writes:

    On 2021-10-29 at 15:29, Tim Rentsch wrote:

    Paavo Helde <myfirstname@osa.pri.ee> writes:

    10.10.2021 09:55 Steve Keller kirjutas:

    Paavo Helde <myfirstname@osa.pri.ee> writes:

    Such global const definitions normally appear in a common header file >>>>> and would just cause duplicate linker symbols if they had external
    linkage. Now they can be just optimized away in translation units
    which do not use them.

    I believe the original motivation was to have an easy replacement of C >>>>> macros in header files, i.e. instead of

    #define a 42

    one can easily use

    const int a = 42;

    which would function almost exactly like a #define, plus it honors C++ >>>>> namespaces and cannot be undefined.

    If you write such "global" const objects into a header file they are
    in fact not global, because the implicit 'static' makes them local to
    file translation unit and this can even cause duplication:

    $ cat foo.hh
    const int a = 42;
    const int *bar();
    $ cat x.cc
    #include "foo.hh"
    const int *bar() { return &a; }
    $ cat y.cc
    #include <iostream>
    #include "foo.hh"
    const int *foo() { return &a; }
    int main()
    {
    std::cout << (void *)foo() << '\n' << (void *)bar() << '\n'; >>>> }
    $ g++ -Os -o foo x.cc y.cc
    $ ./foo
    0x56027d731008
    0x56027d731004

    For a small 'int' one may not care but for larger constant objects
    this may be really bad.

    The unused const objects are routinely optimized away be the compiler,
    as you saw by yourself ("object file was empty").

    It's true that for larger and more complicated objects this might not
    work so well. So don't do that. Put the object in a single TU and
    provide functions to access it. (Making the object extern is not safe
    in general because of the "Static Initialization Order Fiasco".)

    To avoid it, I think you still have to write the defintion into only
    one C++ source file using 'extern'

    extern const int = 42;

    and put

    extern const int a;

    into the header file. I still don't see the advantage for C++ to
    differ from C here.

    In some sense the do not differ. The const globals in C++ are
    primarily meant for replacing C #define. Each C #define is TU-specific
    and gets fully preprocessed by the preprocessor, no other TU-s are
    involved.

    It appears you have sidestepped the central question here. What a
    definition like 'const int foo = 7;' (without any mention anywhere
    of 'extern') does in C++ is, AFAICT, exactly the same as a similar
    definition with static, namely 'static const int foo = 7;'. If
    these two definitions are exactly the same (and I believe they are),
    why change the meaning of the version that doesn't say 'static'?
    Why introduce what appears to be a gratuitous incompatibility with C
    when there was already a perfectly good construct that could be used
    (and works in C++ now) for defining a local constant?

    A problem here is that C++ introduced the const keyword first.

    When C got it later, the C committee decided to do it slighly
    differently.

    These statements leave out some important parts of the story.

    No question that const appeared first in early versions of C++.

    Not long after that however const was assimilated into working C
    compilers (and years before ANSI C was ratified). So a basis for
    comparison was evident pretty early.

    The question here though is not about const but about changing
    the C linkage model. It was obvious from day one that using
    const without a storage class to mean internal linkage is a
    departure from the C linkage model. And an unnecessary one: to
    the best of my knowledge 'static const <type> whatever;' has
    always worked in C++ the same as without the 'static'. Allowing
    a redundant form gives rise to a gratuitous incompatibility.

    In the late 1980s not many people were using C++, and C++ was
    itself in a state of flux. It would have been easy at that time
    to abandon the rule that const-without-static had internal
    linkage, getting rid of the oddball linkage exception, and
    restore compatibility with C rules (by then the ANSI C efforts
    were far enough along that one could see what those rules would
    be post-standardization).

    I must admit it irks me more than a little bit that C++'s idea of
    staying compatible with C is always for C to change to make C
    more like C++, and never the other way around.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Scott Lurndal on Sun Nov 7 05:08:47 2021
    scott@slp53.sl.home (Scott Lurndal) writes:

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:

    Paavo Helde <myfirstname@osa.pri.ee> writes:

    In some sense the do not differ. The const globals in C++ are
    primarily meant for replacing C #define. Each C #define is TU-specific
    and gets fully preprocessed by the preprocessor, no other TU-s are
    involved.

    It appears you have sidestepped the central question here. What a
    definition like 'const int foo = 7;' (without any mention anywhere
    of 'extern') does in C++ is, AFAICT, exactly the same as a similar
    definition with static, namely 'static const int foo = 7;'.

    Is that not dependent upon scope? For example:

    class x {
    static const unsigned long FRED = 0xabcdef00ul;

    };

    Yes, it's true that leaving off 'static' here means something
    significantly different. Note however, (a) neither of these
    forms is allowed in C so there is no question of incompatibility;
    (b) the 'static'-less form wasn't allowed until C++11; (c) only
    the 'static' form works for the purpose of being usable in
    constant expressions, so the question here is moot.

    requires an additional declaration in a compilation unit, e.g.

    const unsigned long x::FRED;

    In my tests such a declaration was needed only if the address of
    the static member FRED was taken. (I confess I didn't even try
    to consult the C++ standard to see if that result is officially
    okay or is merely a consequence of undefined behavior.)

    While

    namespace y {
    const unsigned long FRED = 0xabcdef00ul;
    };

    doesn't require an additional declaration.

    AFAICT declarations inside namespaces behave the same way as
    declarations at file scope, that is, const-without-static
    behaves just the same way as const-with-static (and the same
    rules for declarations/definitions, etc). So I don't think
    this scenario is an exception to what I said earlier.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Tim Rentsch on Sun Nov 7 14:21:29 2021
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    scott@slp53.sl.home (Scott Lurndal) writes:


    Is that not dependent upon scope? For example:

    class x {
    static const unsigned long FRED = 0xabcdef00ul;

    };

    Yes, it's true that leaving off 'static' here means something
    significantly different. Note however, (a) neither of these
    forms is allowed in C so there is no question of incompatibility;
    (b) the 'static'-less form wasn't allowed until C++11; (c) only
    the 'static' form works for the purpose of being usable in
    constant expressions, so the question here is moot.

    requires an additional declaration in a compilation unit, e.g.

    const unsigned long x::FRED;

    In my tests such a declaration was needed only if the address of
    the static member FRED was taken. (I confess I didn't even try
    to consult the C++ standard to see if that result is officially
    okay or is merely a consequence of undefined behavior.)

    Actually, it depends more on context and the compilation options.

    With gcc (on linux), if you compile with -O2 or -O3 (and do
    not use the address operator), you don't need to add the
    declaration. If you don't use -O, then you will likely find
    the linker complaining about a missing symbol. I see this
    quite regularly, to the point that we generally use enum for
    constants (or const definitions in a namespace), even tho
    enum (pre C++xx) doesn't lend itself to type safety.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)