Wednesday, June 24, 2020

Digest for comp.lang.c++@googlegroups.com - 25 updates in 4 topics

already5chosen@yahoo.com: Jun 24 03:11AM -0700

#include <cstdlib>
#include <algorithm>
 
struct bar {
static const int foo = 42;
};
int main()
{
return std::max(rand(), bar::foo);
}
 
 
Both up to date g++ and clang++ -O0 under MSYS2 fail during link claiming 'undefined reference to bar::foo'.
 
With optimization enabled everything works fine, but that's because an optimizer eliminates reference to bar::foo.
 
VS2017 and 2019 link it just fine, regardless of optimization level.
 
 
Now, thinking a little about the root of the problem, I figured out how to write a simpler reproducer that does not depend on optimization level:
 
#include <cstdio>
struct bar {
static const int foo = 42;
};
int main()
{
return printf("%p\n", &bar::foo);
}
Juha Nieminen <nospam@thanks.invalid>: Jun 24 10:45AM

> }
 
> Both up to date g++ and clang++ -O0 under MSYS2 fail during link claiming 'undefined reference to bar::foo'.
 
> With optimization enabled everything works fine, but that's because an optimizer eliminates reference to bar::foo.
 
The problem is that std::max() takes a reference to its parameter, which
means that it needs an actual object, ie. an actual instance of that object.
 
Technically speaking you have to instantiate all class variables
(ie. "static" variables inside a class). The declaration of the variable is not
an instantiation. Just a declaration. That means that if you have:
 
struct bar
{
static int foo;
};
 
that's not enough. You have to also instantiate that variable somewhere:
 
int bar::foo = 42;
 
Static const integral variables are a bit more complicated. With them you
only need to instantiate them if you need their address (ie. you need a
pointer or reference to them), but if that's the case, you have to
instantiate them, else the compiler cannot get an address to it.
That's why you are getting a linker error.
 
> {
> return printf("%p\n", &bar::foo);
> }
 
This is even clearer because here you are trying to get a pointer to variable
that has only been declared but hasn't been instantiated. You have to
instantiate it if you want a pointer to it:
 
const int bar::foo = 42;
already5chosen@yahoo.com: Jun 24 04:15AM -0700

On Wednesday, June 24, 2020 at 1:46:06 PM UTC+3, Juha Nieminen wrote:
> that has only been declared but hasn't been instantiated. You have to
> instantiate it if you want a pointer to it:
 
> const int bar::foo = 42;
 
I know how C++ worked in 20th century. Just hoped that this bad days gone.
It seems, they are only gone in Microsoft world.
 
However even in gcc/clang word it is not quite as bad as you're saying.
The code below passes compilation/link just fine. So, situation is pretty bad, but not quite as bad as in 20th century.
 
#include <cstdio>
struct bar {
static const int foo = 42;
};
const int bar::foo;
int main()
{
return printf("%p\n", &bar::foo);
}
already5chosen@yahoo.com: Jun 24 08:03AM -0700


> Both up to date g++ and clang++ -O0 under MSYS2 fail during link claiming 'undefined reference to bar::foo'.
 
> With optimization enabled everything works fine, but that's because an optimizer eliminates reference to bar::foo.
 
> VS2017 and 2019 link it just fine, regardless of optimization level.
 
So, unless I am missing something, in 2020 C++ still has no nice portable syntax for typed constants that can be declared at class==struct scope and don't come in conflict with basic DRY principle.
 
Here is the best workaround I was able to find:
 
//---- beg
#include <cstdlib>
#include <algorithm>
 
struct bar {
static constexpr int foo() { return 42};
};
int main()
{
return std::max(rand(), bar::foo());
}
//---- end
 
It appears portable, but quite ugly.
I wouldn't use it in practice because it hurts my sense of esthetic.
 
Juha Nieminen <nospam@thanks.invalid>: Jun 24 03:03PM

> It seems, they are only gone in Microsoft world.
 
> However even in gcc/clang word it is not quite as bad as you're saying.
> The code below passes compilation/link just fine. So, situation is pretty bad, but not quite as bad as in 20th century.
 
I have absolutely no idea what you are talking about. The behavior
and requirements of class variables has always been the same. That
hasn't changed. And in your code you did exactly as I instructed:
 
> {
> return printf("%p\n", &bar::foo);
> }
 
So I have no idea what you are talking about, "they are only gone
in Microsoft world". What is gone in "Microsoft world"?
already5chosen@yahoo.com: Jun 24 08:29AM -0700

On Wednesday, June 24, 2020 at 6:03:58 PM UTC+3, Juha Nieminen wrote:
 
> I have absolutely no idea what you are talking about. The behavior
> and requirements of class variables has always been the same. That
> hasn't changed. And in your code you did exactly as I instructed:
 
No, my code has initializer (= 42) within class declaration. Yours has it outside.
Initializer residing within class declaration (so, most likely in the in header file) is better than in implementation (most likely in separate file).
It's better from the perspective of programmer that is going to use the interface, because if he want to see an actual value of the bar::foo, it's here in the "interface" file. He is not forced to look in the implementation file.
It is also better from the perspective of compiler, because even without LTCG or similar technique compiler can see the actual value and use the knowledge for optimization.
 
AFAIR, initializer within class declaration was not allowed in 20th century, except may be last years or two. So, I count it as improvement.
 
> > }
 
> So I have no idea what you are talking about, "they are only gone
> in Microsoft world". What is gone in "Microsoft world"?
 
Bad old days.
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Jun 24 05:30PM +0200

> return std::max(rand(), bar::foo);
> }
 
> Both up to date g++ and clang++ -O0 under MSYS2 fail during link
claiming 'undefined reference to bar::foo'.
> {
> return printf("%p\n", &bar::foo);
> }
 
In spite of the initialization the declaration of `bar::foo` is just a
declaration, not a definition.
 
That means that no storage is allocated.
 
When the address of `bar::foo` is used (even just internally in
pass-by-reference) you introduce a dependency on the storage whose
address is taken, but there's no such, so ideally the linker complains.
 
- - -
 
A compiler & linker may still erroneously accept the code. One reason,
as you note, is that optimization can remove the problem before it's
noticed. Also, it was for a time common practice, and as noted below
there is apparently something in C++17 that allows this code.
 
Visual C++ 2019, in blissful ignorance, accepts both your examples,
compiled as C++11, C++14 and C++17.
 
MinGW g++ 9.2. produces a linker error for C++11 and C++14, but is okay
with the examples as C++17.
 
I do not know of any change in C++17 that should make the examples valid
(modulo the pointer argument in the last, which should be `(void*)`).
 
But perhaps there is, or perhaps it's just a compiler quirk.
 
- - -
 
A C++17 workaround, if C++17 doesn't make the code valid, is to declare
the variable as `inline`.
 
In C++14 and earlier one could use one of
 
* Providing a /definition/ of the variable.
For a header only module this involved placing the variable in a
class template, which is then fully specialized with arbitrary params.
 
* Using an `enum` instead of an integral type constant.
Curiously enumerations are not formally integral types.
 
* Using a `constexpr` function.
 
 
- Alf
already5chosen@yahoo.com: Jun 24 08:35AM -0700

> //---- end
 
> It appears portable, but quite ugly.
> I wouldn't use it in practice because it hurts my sense of esthetic.
 
O.k. I was missing something. There exists a better workaround:
 
#include <cstdlib>
#include <algorithm>
 
struct bar {
static inline const int foo = 42;
};
int main()
{
return std::max(rand(), bar::foo);
}
 
 
It requires C++17, which I have but some of the people I am working with can not have, so I am not sure if I'm going to apply it right now. But that's my problems, not of C++ standard.
"Öö Tiib" <ootiib@hot.ee>: Jun 24 08:48AM -0700

> return std::max(rand(), bar::foo);
> }
 
> Both up to date g++ and clang++ -O0 under MSYS2 fail during link claiming 'undefined reference to bar::foo'.
 
Use constexpr.
C++17 makes the definition redundant for constexpr (but allowed)
https://timsong-cpp.github.io/cppwp/n4659/depr.static_constexpr#1
 
#include <cstdlib>
#include <algorithm>
 
struct bar {
static constexpr int foo = 42;
};
// constexpr int bar::foo; <- unneeded but valid
int main()
{
return std::max(rand(), bar::foo);
}
already5chosen@yahoo.com: Jun 24 08:51AM -0700

On Wednesday, June 24, 2020 at 6:31:08 PM UTC+3, Alf P. Steinbach wrote:
> there is apparently something in C++17 that allows this code.
 
> Visual C++ 2019, in blissful ignorance, accepts both your examples,
> compiled as C++11, C++14 and C++17.
 
Actually, I see that it is in spirit of the language. That's similar to how templates are handled in many situations.
Are you sure that it's explicitly disallowed by the Standard?
 
 
> - - -
 
> A C++17 workaround, if C++17 doesn't make the code valid, is to declare
> the variable as `inline`.
 
Thanks. I already figured it out a hour or so ago. Good to know that it works not just by chance on 3 compilers I tested, but by Standard.
 
 
> * Providing a /definition/ of the variable.
> For a header only module this involved placing the variable in a
> class template, which is then fully specialized with arbitrary params.
 
A bit ugly, isn't it?
 
> * Using an `enum` instead of an integral type constant.
> Curiously enumerations are not formally integral types.
 
enum is untyped. An 'int' type in my example, that happens to be fully compatible with enum, was here just for sake of illustration. The types I want to use in real code, are different.
 
'class enum" can be typed, but it has even worse usability issues.
Requires ALOT of explicit casting just about everywhere.
 
 
> * Using a `constexpr` function.
 
That's my first workaround (see above). Very ugly.
 
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Jun 24 05:56PM +0200

On 24.06.2020 17:48, Öö Tiib wrote:
> {
> return std::max(rand(), bar::foo);
> }
 
 
[P:\temp]
> type foo.cpp
#include <cstdio>
struct bar {
static constexpr int foo = 42;
};
int main()
{
return printf("%p\n", (void*)&bar::foo);
}
 
[P:\temp]
> g++64 foo.cpp -std=c++14
h:/installed/mingw/nuwen/mingw
9-2/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe:
C:\Users\AlfP\AppData\Local\Temp\ccMtRldi.o:foo.cpp:(.rdata$.refptr._ZN3bar3fooE[.refptr._ZN3bar3fooE]+0x0):
undefined reference to `bar::foo'
collect2.exe: error: ld returned 1 exit status
 
 
... with MinGW g++ 9.2.
 
 
- Alf
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Jun 24 06:02PM +0200

>> For a header only module this involved placing the variable in a
>> class template, which is then fully specialized with arbitrary params.
 
> A bit ugly, isn't it?
 
Yes. It was the C++14 and earlier super-ugly way to express the kind of
linkage that one now has with `inline` variables. It was allowed by a
special exemption for templates in the One Definition Rule.
 
 
 
> Requires ALOT of explicit casting just about everywhere.
 
>> * Using a `constexpr` function.
 
> That's my first workaround (see above). Very ugly.
 
Agreed.
 
But, to avoid the Static Initialization Fiasco(tm) problem, it can be a
good idea to use the function, essential a Meyer's singleton, in general.
 
 
- Alf
Sam <sam@email-scan.com>: Jun 24 07:05AM -0400

Mr Flibble writes:
 
> There isn't one and there never should be: we have neoGFX for that! :D
 
Proposals for GUI libraries are accepted only from those who have actually
written one.
Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Jun 24 01:00PM +0100

On 24/06/2020 12:05, Sam wrote:
> Mr Flibble writes:
 
>> There isn't one and there never should be: we have neoGFX for that! :D
 
> Proposals for GUI libraries are accepted only from those who have actually written one.
 
I have written one, dear.
 
/Flibble
 
--
"Snakes didn't evolve, instead talking snakes with legs changed into snakes." - Rick C. Hodgin
 
"You won't burn in hell. But be nice anyway." – Ricky Gervais
 
"I see Atheists are fighting and killing each other again, over who doesn't believe in any God the most. Oh, no..wait.. that never happens." – Ricky Gervais
 
"Suppose it's all true, and you walk up to the pearly gates, and are confronted by God," Byrne asked on his show The Meaning of Life. "What will Stephen Fry say to him, her, or it?"
"I'd say, bone cancer in children? What's that about?" Fry replied.
"How dare you? How dare you create a world to which there is such misery that is not our fault. It's not right, it's utterly, utterly evil."
"Why should I respect a capricious, mean-minded, stupid God who creates a world that is so full of injustice and pain. That's what I would say."
Sam <sam@email-scan.com>: Jun 24 10:38AM -0400

Mr Flibble writes:
 
 
>> Proposals for GUI libraries are accepted only from those who have actually
>> written one.
 
> I have written one, dear.
 
Do you have a Youtube video, that shows it off? Where's the source
repository, I'm curious to look at it, to see if I can pick up some tips,
for mine's.
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Jun 24 05:44PM +0200

On 24.06.2020 16:38, Sam wrote:
 
> Do you have a Youtube video, that shows it off? Where's the source
> repository, I'm curious to look at it, to see if I can pick up some
> tips, for mine's.
 
I guess you can find Leigh's neoGFX easily just by googling it.
 
Like him I've written a number of GUI libraries, but they've been
minimal. In the 1990's I found it interesting to experiment with various
ways to associate a Windows window with C++ object. In particular the
trick used in Borland's framework, namely a /trampoline function/: a
kind of pseudo function (as "window procedure") that just put an object
address in a register and jumped to a common dispatch function.
 
Later my interest for GUI framework design focused on providing the C++
initialization guarantee, that after constructor execution you have a
fully working usable object at hand, with class invariants established.
 
I.e., no MFC-style two-phase or multi-phase construction.
 
Nowadays Windows provides a standard window subclassing mechanism that
uses unspecified means internally, that's at bottom of the very thin
Windows API wrapper code that I'm currently developing and using for a
hobby project of mine. That hobby project is not public, but the so far
very minimal Windows API wrapper code is. Essentially this codes takes
care of the two issues mentioned above, namely associating a window with
a C++ object, and providing C++ construction, though destruction is
modeled as hara-kiri, plus, it works around various Windows bugs.
 
<url: https://github.com/alf-p-steinbach/winapi>.
 
 
- Alf
Juha Nieminen <nospam@thanks.invalid>: Jun 24 06:39AM

> packages, some part is about deploying and executing something
> automatically and some part of it is about generating reports
> of any aspect of it.
 
Problem is, what happens when you need to compile the library for
a system or in a manner that's not supported by the configure script?
 
As mentioned before, my personal experience is trying to compile
libraries to be used in an iOS project. Configure scripts are
beyond useless for that. What's worse, since the library source
code usually heavily relies on the "config.h", Makefile, or
whatever files created by the configure script, and cannot be
compiled without them, it usually makes it extraordinarily
difficult to compile the library for iOS.
 
That's most aggravating in the case of libraries that do nothing
that requires platform-specific stuff and can be implemented in
100% standard C or C++ (eg. many number-crunching libraries operating
on native integer or floating point types for which you can find
perfectly good definitions in the standard library headers).
 
So it's one thing that the configure script is uselss for this.
It becomes worse when the configure script is pretty much
*mandatory* to compile the library.
 
Configure scripts usually seem to assume that you are compiling for the
current host system. When compiling for iOS that's not the case. You don't
compile the project *in* iOS. You compile it in macOS. Detecting the
stuff in macOS is pretty useless for compiling for iOS.
 
It becomes even more complicated given the fact that if you are compiling
the library into eg. a statically linkable library file (ie .a file),
you usually want at least two versions of the binary inside the library
file: One for the iOS device and another for the iPhone simulator.
(This is a simulator, not an emulator. It does not run iOS native ARM
code. It runs an x86 version of your software. It requires compiling
the code for a completely different target, which is still not the
same as the hosting macOS system. The CPU is the same, but pretty
much nothing else is.)
 
Doing this is a bit complicated, and needless to say, configure
scripts have zero support for any of this.
boltar@nowhere.co.uk: Jun 24 10:52AM

On Tue, 23 Jun 2020 17:31:04 +0100
 
>The user interface is what's simple, not necessarily the library,
>although that should be kept small and simple as much as possible (I
>realise I'm posting in a C++ group where that concept is unknown!).
 
You've missed the point. Everything I mentioned you have to do BEFORE you even
worry about a user interface.
 
>It is necessary to know where to draw the line, otherwise your new
>simple library will be be as comprehensive as all the rest.
 
Yes, you draw it in a window. How big will that window be? Do you want it to
appear as soon as the library is loaded or do you want an explicit creation
command? Will it accept input or be output only etc etc etc. I think its fairly
clear you don't understand how windowing systems work but they're hardly
bleeding edge - this is stuff Xerox solved in the 70s.
 
>This is where looking at libraries from the past can be useful. No, they
>probably didn't handle resizing, but they still got things done!
 
Then they probably weren't window based libraries.
 
>(For that matter, smartphone apps tend not do resizing either; they
>mostly run full-screen, one app at a time, just like those old machines.)
 
Which is why they're shit for doing any useful work.
 
>colossal library, so this wouldn't be for you. Some of us just want the
>next step beyond a text console, which is only slightly more advanced
>than a 1970s VDU, itself not much more capable than a paper teletype.
 
Nobody - apart from you - wants a standard C++ graphics library that does
nothing except draw graphics primitives into a fixed size output only window.
 
>> Ugh. Who would bother?
 
>You'd bother if creating higher res images than your display. But if
 
Use a pdf or postscript library then.
Bart <bc@freeuk.com>: Jun 24 12:47PM +0100

> command? Will it accept input or be output only etc etc etc. I think its fairly
> clear you don't understand how windowing systems work but they're hardly
> bleeding edge - this is stuff Xerox solved in the 70s.
 
Hardly anyone among the general public used anything like that. Apart
from toy home computers, that didn't happen until they started using
Windows, and particularly Win95.
 
My job for about 15 years was creating graphics apps, starting on PCs in
the mid-80s which had very little graphics support (so I had to provide
drivers and libraries). The apps were full-screen mode - there was no
inter-process windowing. Actually there weren't any other processes,
only yours.
 
 
>> This is where looking at libraries from the past can be useful. No, they
>> probably didn't handle resizing, but they still got things done!
 
> Then they probably weren't window based libraries.
 
They would have had one implicit window covering the whole screen. Ever
used a Tektronix terminal? They didn't have windows either! Such things
were still considered a highly useful alternate to using a text display.
 
You seem to think that there only two possibilities - either a
1960s-style text display at one end, and a full blown 2020s windowing
GUI system at the other. Nothing in the middle.
 
BTW consider a program line like this which writes into an windowed
console/terminal window on a GUI graphics display:
 
printf("Hello, World!\n");
 
Tell where in that code you specify which window you you write to, how
big the window is, and how it handles resizing or overlaps. Oh, you don't?
 
Now imagine you had this line:
 
setpixel(124, 456, 0xFF00FF);
 
Do you get it now?
 
 
>> You'd bother if creating higher res images than your display. But if
 
> Use a pdf or postscript library then.
 
And how big will those be, how complex, and how standard? The script
below draws a solid, outlined circle into a giant bitmap. The functions
used are thin-ish wrappers around WinAPI, and using 1990s GDI (though
using dynamic code).
 
w:=gxcreatewindow()
 
bm:=bmcreate(24,16384,16384) # create in-memory image
gxclear(bm, getrgb(white)) # clear to white
 
r:=bm.dimx%2-10
gxfillcircle(bm,r,r,r,getrgb(green),1)
 
bmsave("circle.bmp",bm)
 
gxcopy(w,bm,scalex:0.01) # display copy at 1% scale
 
eventloop()
 
(Jpeg o/p had a problem. The bmp output is 800MB.)
boltar@nowhere.co.uk: Jun 24 02:47PM

On Wed, 24 Jun 2020 12:47:33 +0100
>drivers and libraries). The apps were full-screen mode - there was no
>inter-process windowing. Actually there weren't any other processes,
>only yours.
 
Its not the mid-80s anymore.
 
 
>They would have had one implicit window covering the whole screen. Ever
>used a Tektronix terminal? They didn't have windows either! Such things
>were still considered a highly useful alternate to using a text display.
 
Great, but its not the mid-80s anymore. If you want to do graphics then every
consumer OS requires you to deal with windows when doing graphics whether you
like it or not. The only exception is doing VGA graphics in a linux console
or running a raw X server with no window manager but they're both frankly of
little use in this day and age.
 
>You seem to think that there only two possibilities - either a
>1960s-style text display at one end, and a full blown 2020s windowing
>GUI system at the other. Nothing in the middle.
 
Windowing != GUI, at least not with Xwin.
 
 
> printf("Hello, World!\n");
 
>Tell where in that code you specify which window you you write to, how
>big the window is, and how it handles resizing or overlaps. Oh, you don't?
 
Thats because - on unix at least - stdout writes to the tty which may or may
not be linked to a virtual or physical terminal. Normally that terminal will
be the one you run the program from within but it doesn't have to be the case.
 
>Now imagine you had this line:
 
> setpixel(124, 456, 0xFF00FF);
 
>Do you get it now?
 
And where does it writes to? Are you going to implement a shell inside your
graphical window in order to kick it off? You might wish that we were still
in the past with a single output destination but consumer OS's are not like
that. But feel free to play about with some embedded kit like Micromite that
support bitmap displays out the box but don't expect any sane person to suggest
adding in your mickey mouse API to the C++ standard.
 
>>> You'd bother if creating higher res images than your display. But if
 
>> Use a pdf or postscript library then.
 
>And how big will those be, how complex, and how standard? The script
 
Umm, pdf has been a standard interchange format for a very long time.
Postscript seems to be fading away a bit.
 
>below draws a solid, outlined circle into a giant bitmap. The functions
>used are thin-ish wrappers around WinAPI, and using 1990s GDI (though
>using dynamic code).
 
You're stuck in the past. Time for you to catch the clue train back to 2020.
"Öö Tiib" <ootiib@hot.ee>: Jun 24 08:10AM -0700

On Wednesday, 24 June 2020 09:40:01 UTC+3, Juha Nieminen wrote:
> > of any aspect of it.
 
> Problem is, what happens when you need to compile the library for
> a system or in a manner that's not supported by the configure script?
 
Then I dump that script. I have to invest all man-hours that it
takes to make build system for the library that suits my needs. That is
why it is bad that exactly nothing of it is standardized. I likely
have to make everything from scratch. Autotools configure script
is good example of painful and useless tool in situation for what
it wasn't meant.
Frederick Gotham <cauldwell.thomas@gmail.com>: Jun 24 01:15AM -0700

On Tuesday, June 23, 2020 at 4:08:09 PM UTC+1, Alf P. Steinbach wrote:
 
> auto main()
> -> int
> {
 
 
 
I claim that I'm the person who started this trend. (Although personally I always put 'void' instead of empty parentheses).
Juha Nieminen <nospam@thanks.invalid>: Jun 24 09:37AM

>> -> int
>> {
 
> I claim that I'm the person who started this trend. (Although personally I always put 'void' instead of empty parentheses).
 
Clearly there's too much information in one single line there. Rather obviously it should be:
 
using
Main_Ret_Value_t
=
int;
 
auto
main
(
void
)
->
Main_Ret_Value_t
{
// your code here
}
Bonita Montero <Bonita.Montero@gmail.com>: Jun 24 12:29PM +0200

> {
> // your code here
> }
 
I hope your joking, otherwise you'd get the studpidity-award.
Sam <sam@email-scan.com>: Jun 24 07:04AM -0400

Scott Newman writes:
 
>>     foo<decltype( []{})> baz;
>> }
 
> This ain't a lambda.
 
It is, if you actually know C++.
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: