Monday, September 14, 2020

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

James Kuyper <jameskuyper@alumni.caltech.edu>: Sep 14 12:16AM -0400

On 9/11/20 2:57 PM, Chris Vine wrote:
> On Fri, 11 Sep 2020 11:44:54 -0400
> James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
>> On 9/11/20 6:17 AM, Chris Vine wrote:
...
> "otherwise, the behavior is undefined" seem to have been read in the
> context of arrays only and not applying to raw malloc'ed memory. That
> reading is impossible with C++20's [expr.add]/4. ...
 
I don't see that as being the case. It says:
"When an expression J that has integral type is added to or subtracted
from an expression P of pointer type, the result has the type of P.
(4.1) — If P evaluates to a null pointer value and J evaluates to 0, the
result is a null pointer value.
(4.2) — Otherwise, if P points to an array element i of an array object
x with n elements (9.3.3.4), 76 the expressions P + J and J + P (where J
has the value j) point to the (possibly-hypothetical) array element i +
j of x if 0 ≤ i + j ≤ n and the expression P - J points to the
(possibly-hypothetical) array element i − j of x if 0 ≤ i − j ≤ n.
(4.3) — Otherwise, the behavior is undefined." (7.6.6p4).
 
Just like the corresponding words in the C standard, paragraph 4.2 is
predicated on P pointing at the i'th member of an array. If there is no
such array, then 4.3 applies, and the behavior is undefined.
If C++2020 handles this issue better than C does, the different wording
that makes that true must lie in some other part of the document.
 
> also that the "effective type" of raw memory is normally the type of
> the first object constructed in it (�6.5/6 of C11), so in a sense
> malloc'ed memory becomes an array in C by being treated as an array.
 
The fact that this isn't normally the case is precisely what's
problematic in C. When an lvalue of a given type is used to write to
dynamically allocated memory, that write gives that memory the effective
type of that lvalue. If you only write one element of an array at a
time, each element of that array acquires an effective type which is the
array's element type, but nothing acquires an effective type that is the
array type.
There are ways around this: you can write an entire struct object to the
memory using a single assignment expression, in which case that object
acquires that struct type as its effective type, and in particular, if
one of the members is an array, that member's memory acquires that array
type.
Also, when you copy an object into dynamically allocated memory using
memcpy(), memmove(), or otherwise copied as an array of character type,
there's a special rule that says that the memory acquires the effective
type of that object. As a special case of this, an array object can be
copied into the memory, giving it an array type.
Lots of C code treats dynamically allocated memory as if it were an
array, without either of those two special cases applying. Therefore, a
strict reading of the rules of pointer arithmetic gives access to any
element of such an array other than the first element undefined behavior.
 
> (Somewhat akin to the new C++ implicit-lifetime types.)
 
6.7.2p12 has code that contains a comment indicating that
 
X *p = (X*)std::malloc(sizeof(struct X))
 
implicitly creates an object of type X in the allocated memory.
20.10.12p5 appears to be the clause that actually supports that comment.
However, it's not clear to me from 20.10.12p5 that
 
X *p = (X*)std::malloc(n*sizeof(struct X));
 
would implicitly create an n-element array. I certainly wouldn't object
to that being the case, but it doesn't clearly say so.
 
 
> is undefined behaviour because of �6.5.6/8, as in C it is the only way
> of getting to the second element in advance of constructing the first
> one.
 
I think it is undefined behavior. I don't think it was intended to be
undefined behavior, so this represents a defect in the standard, but I
don't see any way to justify saying that there's an array that can be
used to give meaning to such pointer arithmetic.
 
> taken to arise for the case of an array of trivial types, given that
> array types are now implicit-lifetime types. I will need to consider
> revised [intro.object]/10 further on this.
 
Could you explain what that section says that makes you feel that way?
 
>> C++2020 different, to render that clause inapplicable?
 
> My meaning was that it was not applicable to my example, which did not
> involve construction of an object.
 
Now that I have a copy of n4860.pdf, I see that there's a much more
serious obstacle to having that clause apply: there is no such clause in
C++2020. The location in n4860.pdf that corresponds to 5.7 in n3797.pdf
is 7.6.6, but it contains no such wording, nor does similar wording
appear anywhere else. Given "int i;", is there no longer any meaning
defined for 1 + &i, or have I missed something that defines it? I've
just started reading this version of the standard - I wouldn't be
surprised if I missed something.
Ike Naar <ike@rie.sdf.org>: Sep 14 05:38AM

> evaluated only once, and they both define addition of integer values to
> pointer values in terms of a containing array; if there's no such array,
> there's no applicable definition of what the addition means.
 
If "equivalent" means "identical in value", i++ and i=i+1 are not equivalent.
The value of i++ is the value of i before increment.
The value of i=i+1 is the value of i after increment.
James Kuyper <jameskuyper@alumni.caltech.edu>: Sep 14 09:38AM -0400

On 9/14/20 1:38 AM, Ike Naar wrote:
 
> If "equivalent" means "identical in value", i++ and i=i+1 are not equivalent.
> The value of i++ is the value of i before increment.
> The value of i=i+1 is the value of i after increment.
 
I meant equivalent as a stand-alone expression, such as the one that was
being discussed. My purpose was to justify cross-referencing what both
standards say about adding integers to pointers, which is not described
directly in the paragraphs describing postfix increment; I wasn't
interested in going into the other aspects of that operator.
 
If I'd been inclined to discuss postfix increment operators in a more
general sense, the simplest way would have been to quote the entire
relevant paragraphs:
 
C:
"The result of the postfix ++ operator is the value of the operand. As a
side effect, the value of the operand object is incremented (that is,
the value 1 of the appropriate type is added to it). See the discussions
of additive operators and compound assignment for information on
constraints, types, and conversions and the effects of operations on
pointers. The value computation of the result is sequenced before the
side effect of updating the stored value of the operand. With respect to
an indeterminately-sequenced function call, the operation of postfix ++
is a single evaluation. Postfix ++ on an object with atomic type is a
read-modify-write operation with memory_order_seq_cst memory order
semantics."
 
C++:
"The value of a postfix ++ expression is the value of its operand.
[Note: The value obtained is a copy of the original value. — end note]
The operand shall be a modifiable lvalue. The type of the operand shall
be an arithmetic type other than cv bool, or a pointer to a complete
object type. An operand with volatile-qualified type is deprecated; see
D.5. The value of the operand object is modified (3.1) by adding 1 to
it. The value computation of the ++ expression is sequenced before the
modification of the operand object. With respect to an
indeterminately-sequenced function call, the operation of postfix ++ is
a single evaluation.
[Note: Therefore, a function call cannot intervene between the
lvalue-to-rvalue conversion and the side effect associated with any
single postfix ++ operator. — end note] The result is a prvalue. The
type of the result is the cv-unqualified version of the type of the
operand. If the operand is a bit-field that cannot represent the
incremented value, the resulting value of the bit-field is
implementation-defined."
Tim Rentsch <tr.17687@z991.linuxsc.com>: Sep 13 09:31PM -0700

Juha Nieminen <nospam@thanks.invalid> writes:
 
[...]
 
> C programmers also seem to be, for some reason, not very intimate
> with the newer versions of the C standard, even though they add
> relatively little to the language.
 
An ironic statement, considering what comes next.
 
> pointers to the function will point to different places. Declaring
> the function as merely 'inline' is the better option because in this
> case duplicates are discarded for the final executable.)
 
Despite what you may think, in C 'static inline' is usually a
better choice than just 'inline'. There are restrictions on
'inline' functions that do not apply to 'static inline'
functions, and, more importantly, simply changing 'static
inline' to 'inline' /can/ lead to linker errors - or worse,
since using such functions can be undefined behavior.
 
Also, despite what you may think, the rules in C for inline
functions (without 'static') are not the same as the rules in C++
for (without 'static') inline functions.
Real Troll <real.troll@trolls.com>: Sep 14 12:20AM -0500

On 09/09/2020 18:08, olcott wrote:
 
> I spoke with someone on the comp.lang.c recently about the benefits of
> c++. Her objections was that c++ has a huge learning curve.
 
One thing c programmers are not prepared to accept is that "they are
Cobol programmers of 2030".  Linus was discussing this with another
Linux developer and they found that young people are not willing to
learn C when there are other languages such as C# . So old C programmers
have nothing to fear for the next 10 years as they are not likely to
face any competition from the young developers.  However, the technology
is changing so old programmers may not find it easy to adapt to the new
requirements.
olcott <NoOne@NoWhere.com>: Sep 14 12:24AM -0500

On 9/14/2020 12:20 AM, Real Troll wrote:
> face any competition from the young developers.  However, the technology
> is changing so old programmers may not find it easy to adapt to the new
> requirements.
 
Yet it seems unwise to create operating systems using C#, yet these same
systems could be easily transformed into C with classes, thus overcoming
the objecttions of Linus Torvald to C++.
 
 
--
Copyright 2020 Pete Olcott
David Brown <david.brown@hesbynett.no>: Sep 14 07:38AM +0200

On 14/09/2020 07:24, olcott wrote:
>> likely to face any competition from the young developers.  However,
>> the technology is changing so old programmers may not find it easy to
>> adapt to the new requirements.
 
It makes little sense to write application code in C, in this century -
there needs to be extraordinary reason for doing so. But it is still a
good choice for a lot of low-level work, embedded work,
performance-critical work (in libraries), and some kind of portable
work. In particular, with C you can make code that is more "stand
alone" and independent of other libraries and ABI details that can cause
issues for other languages, including C++ (no matter how little of the
language you use).
 
> Yet it seems unwise to create operating systems using C#, yet these same
> systems could be easily transformed into C with classes, thus overcoming
> the objecttions of Linus Torvald to C++.
 
It would be crazy, IMHO, to use a managed language like C# for an OS.
An OS needs to be lower level than that. (Higher level languages can be
used for OS services.) C++ works fine for OS's, but the API - the
interface between the OS and the rest of the system - is best held in
pure C as the most portable interface language.
Stuart Redmann <DerTopper@web.de>: Sep 14 08:54AM +0200


>> I hadn't come across that little article before. Thanks, it was fun.
 
> Yes, indeed. Sometimes small gems come out of even the most useless
> threads!
 
+1
 
Wanted to say thanks, too.
 
Stuart
Juha Nieminen <nospam@thanks.invalid>: Sep 14 07:40AM

> of thing). Thus the risk of duplication should normally be zero (and if
> you have a compiler with a warning like gcc's -Winline, it is actually
> zero).
 
Not all compilers are gcc and clang. For example the sdcc compiler (which
is relatively widely used out there) will happily instantiate every
'static inline' function in every compilation unit where it's defined,
and its linker is not advanced enough to remove these instantiations even
if they are never called.
 
I know, because I had to fix this problem in a work-related project
(for a target that has 32 kilobytes of space for the executable binary).
 
Removing the 'static' made it work correctly.
 
> definition. If you have used "inline" alone, and need a non-inlined
> version, you can't get that from the original inline definition - you
> need to re-declare it in a non-inline version.
 
In which situation do you need a "non-line version" of the function?
If you need, for example, a pointer to the function, you can create one
just fine, and it will work just like in C++. If the compiler needs to
actually instantiate the inline function, it will do so, and it will be
a unique instantiation across the entire program (ie. there will be no
duplicates).
 
If I'm wrong with this, please correct me.
Juha Nieminen <nospam@thanks.invalid>: Sep 14 07:43AM

> Despite what you may think, in C 'static inline' is usually a
> better choice than just 'inline'.
 
If you use 'static inline' you may get unwanted duplications of the function
implementation. For example the sdcc compiler does this (even when the
function instantiation isn't actually called anywhere).
 
Sure, that might not matter much in a multi-gigabyte PC. It starts mattering
a lot more when you have eg. 32 kilobytes of space for your executable
binary.
 
> functions, and, more importantly, simply changing 'static
> inline' to 'inline' /can/ lead to linker errors - or worse,
> since using such functions can be undefined behavior.
 
Care to give some examples?
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Sep 14 12:47AM -0700

On 9/13/2020 10:38 PM, David Brown wrote:
 
> It would be crazy, IMHO, to use a managed language like C# for an OS.
> An OS needs to be lower level than that. (Higher level languages can be
> used for OS services.) C++ works fine for OS's,
 
probably should not use exceptions in C++ wrt creating an OS?
 
 
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Sep 14 12:48AM -0700

On 9/14/2020 12:47 AM, Chris M. Thomasson wrote:
>> An OS needs to be lower level than that.  (Higher level languages can be
>> used for OS services.)  C++ works fine for OS's,
 
> probably should not use exceptions in C++ wrt creating an OS?
 
Well, at least when creating the Kernel.
 
"Christian Hanné" <the.hanne@gmail.com>: Sep 14 09:58AM +0200

If someone new to C would learn C++ I'd recommend Visual C++
becuase it's a more visual language. It's rather like Logo.
David Brown <david.brown@hesbynett.no>: Sep 14 11:34AM +0200

On 14/09/2020 09:40, Juha Nieminen wrote:
>> you have a compiler with a warning like gcc's -Winline, it is actually
>> zero).
 
> Not all compilers are gcc and clang.
 
Indeed. But gcc is pretty common - and many other compilers (like
Intel's on pc's, and CodeWarrior for several Motorola/Freescale/NXP
microcontrollers) match gcc semantics and at least some extensions. For
many C programmers, whether you think it is a good thing or not,
"practical C" means "gcc C".
 
> For example the sdcc compiler (which
> is relatively widely used out there)
 
SDCC is not very widely used. It is an impressive project, and I'd be
less reluctant to use 8051 and similar cores if SDCC were the industry
standard there instead of Keil and IAR, but it is a limited project used
for a dying breed of devices.
 
 
> I know, because I had to fix this problem in a work-related project
> (for a target that has 32 kilobytes of space for the executable binary).
 
> Removing the 'static' made it work correctly.
 
SDCC puts a lot of effort into working /correctly/ - even if that does
not mean working /efficiently/. Don't get these mixed up. (Though of
course in embedded systems, performance or code size can be an absolute
requirement too.)
 
Writing code for these kind of microcontrollers invariably requires
writing "SDCC 8051 C" or "Keil 8051 C" or "IAR 6800 C". You don't
expect more than minimal portability and approximate C standards
conformity (SDCC tends to aim a bit more towards standards conformity
even at efficiency costs). You expect that different compilers generate
wildly different efficiencies for the same code, and that small changes
to code that have no semantic difference (and perhaps no object code
difference for optimised gcc code on an x86) can make a big difference
to the resulting code.
 
 
Getting good code from a compiler like SDCC is a niche skill - it's fun
in its way, but not relevant to a wider discussion of C.
 
> actually instantiate the inline function, it will do so, and it will be
> a unique instantiation across the entire program (ie. there will be no
> duplicates).
 
Try this on godbolt.org:
 
inline int foo(int x) { return x * 2; }
 
typedef int (*fint)(int);
fint foobar(void) { return foo; }
 
You get this code:
 
foobar:
mov eax, OFFSET FLAT:foo
ret
 
The function "foo" is not created - so linking will fail. You need to
define it specifically, in one (and only one) C file, just like any
other externally linked function that is used somewhere in the code. If
the inline definition is visible, you can do that simply by writing:
 
"int foo(int x);"
 
This is different from C++ - in C++, a non-inlined version of the
function "foo" will be generated as needed, from a plain "inline" function.
 
Or in C you can avoid including the "inline" header definition and
define a completely different externally linked "foo". (This is not
allowed in C++.)
 
With "static inline", the non-inline version will always be generated as
needed, but be independent from any other non-inline copies generated in
other translation units. (This is true for C and C++.)
 
 
> If I'm wrong with this, please correct me.
 
The point you are missing, as far as I can see (and I too hope others
will correct me if I am wrong), is that C++ supports having an
externally linked function generated in multiple object files. This is
vital to templates, and is also used for inline functions, static data,
and the new weirdly-named "inline variables". So with C++, it's fine
for the compiler to generate an externally linked non-inlined "foo"
function here, included in many translation units, and it is up to the
linker to pick one and throw out the others. (The ODR requires that
they all be the same, because there is no way to specify which is kept.)
 
In C, this is not allowed - you cannot have the same externally linked
symbol defined in more than one object file in the program. So the C
behaviour is different here.
 
 
(In most cases, of course, these differences don't matter - usually when
you have an inline function, you are not planning on having a
non-inlined version anyway.)
David Brown <david.brown@hesbynett.no>: Sep 14 11:36AM +0200

On 14/09/2020 09:43, Juha Nieminen wrote:
>> inline' to 'inline' /can/ lead to linker errors - or worse,
>> since using such functions can be undefined behavior.
 
> Care to give some examples?
 
You forget you are replying to Tim. He expects you to do your own
homework and post the results. You can look forward to a reply in a
couple of months, when you have completely forgotten this thread, saying
"are you sure about that?".
David Brown <david.brown@hesbynett.no>: Sep 14 11:38AM +0200

On 14/09/2020 09:47, Chris M. Thomasson wrote:
>> An OS needs to be lower level than that.  (Higher level languages can be
>> used for OS services.)  C++ works fine for OS's,
 
> probably should not use exceptions in C++ wrt creating an OS?
 
It is common to disable exceptions (and RTTI) in such low-level C++
code, but I don't see it as an absolute requirement. It depends on the
kind of OS you are writing.
 
Real Troll <real.troll@trolls.com>: Sep 14 12:55AM -1000

On 14/09/2020 06:24, olcott wrote:
 
> Yet it seems unwise to create operating systems using C#,
 
That's what Microsoft is doing with Windows 10 OS.  they have started
with the "Store" and some aspects of Windows UI.  Rumours are abound
that some system files are also rewritten in C#. However, there are more
than billion lines of code to be written or re-written using c# so it
takes time!!.  Microsoft can't hire all the C# programmers to speed up
the process.
Richard Harnden <richard.nospam@gmail.com>: Sep 14 02:17PM +0100

On 14/09/2020 11:55, Real Troll wrote:
> than billion lines of code to be written or re-written using c# so it
> takes time!!.  Microsoft can't hire all the C# programmers to speed up
> the process.
 
An IBM man-year .. 730 guys trying to get it done by lunch.
"Öö Tiib" <ootiib@hot.ee>: Sep 14 05:42AM -0700

I wanted somehow at weekend to find maximum of two int32_t-s without
branches (like ifs or ? operators) in C++.
Using gcc builtins I was able to write something quite quickly. It
looks like C but it compiles as C++ too.
 
#include <inttypes.h> // for int32_t related stuff

int32_t mad_max(int32_t a, int32_t b)
{
int32_t d, result;
int32_t o = -1 * __builtin_ssub_overflow( a, b, &d);
__builtin_ssub_overflow( a, (o ^ (d >> 31)) & d, &result);
return result;
}
 
It has no undefined behaviours I hope and where it does compile there
it seems to work. Demo:
<http://coliru.stacked-crooked.com/a/19eca82b689292e5>
 
However with raw C++ I am in trouble. Does anyone have some idea
how to do the trick in raw C++?
Melzzzzz <Melzzzzz@zzzzz.com>: Sep 14 12:49PM

><http://coliru.stacked-crooked.com/a/19eca82b689292e5>
 
> However with raw C++ I am in trouble. Does anyone have some idea
> how to do the trick in raw C++?
 
You have pmaxsd instrunction and I guess intrinsic for x86. No need for
this abomination and much more efficient.
 
 
--
current job title: senior software engineer
skills: c++,c,rust,go,nim,haskell...
 
press any key to continue or any other to quit...
U ničemu ja ne uživam kao u svom statusu INVALIDA -- Zli Zec
Svi smo svedoci - oko 3 godine intenzivne propagande je dovoljno da jedan narod poludi -- Zli Zec
Na divljem zapadu i nije bilo tako puno nasilja, upravo zato jer su svi
bili naoruzani. -- Mladen Gogala
Bonita Montero <Bonita.Montero@gmail.com>: Sep 14 02:49PM +0200

#include <cstdint>
 
int f( int32_t a, int32_t b )
{
int32_t mask = ~(a < b ? -1 : 0);
return a & mask | b & ~mask;
}
 
The ternary operation is often substituted by a "sbb regx, regx".
Depends on the compiler you use.
Bonita Montero <Bonita.Montero@gmail.com>: Sep 14 02:53PM +0200

>     int32_t mask = ~(a < b ? -1 : 0);
>     return a & mask | b & ~mask;
> }
 
Or better:
 
int f( int32_t a, int32_t b )
{
int32_t mask = a < b ? -1 : 0;
return a & ~mask | b & mask;
}
"Öö Tiib" <ootiib@hot.ee>: Sep 14 06:01AM -0700

On Monday, 14 September 2020 15:49:51 UTC+3, Melzzzzz wrote:
> > how to do the trick in raw C++?
 
> You have pmaxsd instrunction and I guess intrinsic for x86. No need for
> this abomination and much more efficient.
 
I did try to ask for C++ not assembler nor compiler builtins.
Tim Rentsch <tr.17687@z991.linuxsc.com>: Sep 13 09:03PM -0700

Juha Nieminen <nospam@thanks.invalid> writes:
 
[..in an earlier posting..]
 
> even large programs manageable, maintainable and the code
> reusable.
 
> Back when OOP was first developed, in the 70's and 80',
 
The purported history is wrong. Modular programming was not a
precursor to OOP. Preliminary work on what came to be OOP was
done in the early 1960's, by Alan Kay, and independently by Ivan
Sutherland with Sketchpad. The programming language Simula had
classes, virtual functions, and inheritance in 1967. The earliest
mention I'm aware of even of the term modular programming was in
1968, and modules themselves were years later. I was hearing Alan
Kay talk about Smalltalk and OOP before the programming language
Modula existed. By then Smalltalk had already gone through two
iterations, and even the first version, Smalltalk 72, was firmly
object-oriented.
 
I won't comment on your description of what OOP is, but certainly
there is a sharp contrast with what Alan Kay had to say about
Smalltalk, generally recognized as the canonical object-oriented
language:
 
Though Smalltalk's structure allows the technique now known as
data abstraction to be easily (and more generally) employed,
the entire thrust of its design has been to supersede the idea
of data and procedures entirely and to replace these with the
more generally useful notions of activity, communication and
inheritance.
 
Alan Kay, 1972
Juha Nieminen <nospam@thanks.invalid>: Sep 14 07:26AM

> more generally useful notions of activity, communication and
> inheritance.
 
> Alan Kay, 1972
 
That doesn't sound contradictory to what I described. He's describing
higher-level notions of OOP, namely how programs ought to be designed,
how "objects" should be thought of and handled. In other words, rather
than think of "objects" as being just data containers, they should be
tought of as entities that are interacted with and which behave in a
particular manner. In other words, data abstraction, ie. data hiding.
(In other words, the actual data inside the object, and the way it's
implemented, is hidden behind a more abstract public interface that
tells the object *what* to do, not *how* it's being done.)
 
I, however, was talking about how all that is actually implemented
behind the scenes. In other words, how all the data members of a class
are physically (well, in "physically" in terms of their location in
RAM) gathered within an object. In other words, an "object" is in
practice, for all intents and purposes, a C struct with a layer of
compile-time abstraction on top of it.
 
I suppose that from the higher-level perspective there's nothing stopping
the practical implementation from gathering the data members in some
other manner (such as putting the same members of all objects in one
common array), but that's not how OO programming languages do it, because
it has never been very practical from an implementation perspective.
 
The problem with this is that, while this type of OO implementation was
just fine and dandy in the 70's, 80's and largely the 90's, nowadays it
leads to inefficient code due to how processor architectures have advanced.
CPUs like consecutive memory accesses and dislike memory accesses that
jump in large steps, or randomly. How OOP has been implemented inevitably
leads to the latter, which is bad for performance.
 
Another issue is that modern processors like to see in advance where the
program execution is going to go. If they don't see it, or if they
"guess" it wrong, it causes a performance penalty. Virtual functions
largely work against this, causing even more performance hits.
 
The other modern advance is in compilers. While they do their best, typical
OOP implementations oftentimes hinder compiler optimizations, eg. when it
comes to autovectorization. It's relatively easy to show practical
examples of this.
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: