Friday, August 30, 2019

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

Mel <mel@zzzzz.com>: Aug 30 07:42AM +0200

On Thu, 29 Aug 2019 11:02:30 +0200, David Brown
> One thing that I gather Rust does well, that C++ has trouble with,
is
> that when you do the Rust equivalent of std::move'ing something, any
> attempt to use the moved-from object is flagged by the compiler. I
> think that would be good to spot in C++.
 
Rust move is built into language. Moved objects destructor is not
called.
 
--
Press any key to continue or any other to quit
Mel <mel@zzzzz.com>: Aug 30 08:04AM +0200

On Thu, 29 Aug 2019 13:29:11 -0700, Keith Thompson <kst-u@mib.org>
wrote:
> > On 29/08/2019 04:36, Melzzzzz wrote:
> [...]
> >> Rust is nowhere near C. Actually, nowhere near C++. It is nice
language,
> >> but not for low level programming. During years they ditched GC
and most
> >> of the things that cripples determinism and performance, it is
ok, but
> >> without pointer arithmetic and null pointers it is high level
language.
> >> Having to convert pointer to int and back in order to perform
arithmetic
 
> What's terrible about it? Rust doesn't particularly need pointer
> arithmetic; unlike C, it's array indexing isn't defined in terms of
> pointers.
 
 
You need pointers for low level things. In rust you need unsafe
blocks for almost anything.
 
--
Press any key to continue or any other to quit
Mel <mel@zzzzz.com>: Aug 30 08:07AM +0200

On Thu, 29 Aug 2019 14:54:18 +0200, Bonita Montero
> > Rust is nowhere near C. Actually, nowhere near C++. It is nice
language,
> > but not for low level programming. During years they ditched GC
and most
> > of the things that cripples determinism and performance, ...
 
 
 
 
> Rust has no GC, but works with counted references.
 
Yes, but it is not much used. Box, owned reference is built in, i
zhink that rc is not built in
 
--
Press any key to continue or any other to quit
Mel <mel@zzzzz.com>: Aug 30 08:10AM +0200

On Thu, 29 Aug 2019 06:08:13 -0700 (PDT), Öö Tiib<ootiib@hot.ee>
wrote:
> > > ""Rust is the future of systems programming, C is the new Assem=
> bly":
> > > Intel principal engineer, Josh Triplett"
 
https://hub.packtpub.com/rust-is-the-future-of-systems-programming-c-is
=
 
> > > "At Open Source Technology Summit (OSTS) 2019, Josh Triplett, a
> > > Principal Engineer at Intel gave an insight into what Intel is
> > > contributing to bring the most loved language, Rust to full
parity with=
 
 
> > > C. In his talk titled Intel and Rust: the Future of Systems
Programming=
> ,
> > > he also spoke about the history of systems programming, how C
became th=
 
> > How about, instead:
> > We take C;
> > We add some features to support things like bounds-checked arrays
and
> > similar;
> > We mostly call it done.
 
 
> Rust supports lot of interesting safety features (like RAII, smart
pointers=
> ,
> ownership, generics, move semantics, thread safety).
> Adding some safe features at side does not help as history with C++
shows.
> Result is bigger language with all the unsafe stuff still there and
valid.
> OTOH if to cripple the unsafe features a bit then result is some
kind of
> fourth language that is neither C, C++ nor Rust.
 
Rust generics are interface based. You cant do anything with generic
type unless you tell which interfaces satisfies.
 
--
Press any key to continue or any other to quit
Keith Thompson <kst-u@mib.org>: Aug 29 11:11PM -0700

>> arithmetic; unlike C, it's array indexing isn't defined in terms of
>> pointers.
 
> You need pointers for low level things.
 
You need pointer *arithmetic* for far fewer things than you need
pointers for.
 
> In rust you need unsafe
> blocks for almost anything.
 
So use unsafe blocks.
 
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */
Mel <mel@zzzzz.com>: Aug 30 08:13AM +0200

On Thu, 29 Aug 2019 14:53:05 +0200, Bonita Montero
> Rust is nice, but Rust has no exceptions. I wouldn't like to
evaluate
> this combined error- and return-values after each function-call that
> might fail.
 
That's Rust advantage. Returnig discriminated union is right way for
handling errors. Rust has syntactic sugar in that. Latest proposal
for c++ exceptions leans toeard that solution.
 
--
Press any key to continue or any other to quit
Bonita Montero <Bonita.Montero@gmail.com>: Aug 30 08:38AM +0200

> Returnig discriminated union is right way for handling errors.
 
NO, that's a mess. Exceptions are by far more comfortable.
The best variant are the distinction between checked and
unchecked exceptions in Java.
Bonita Montero <Bonita.Montero@gmail.com>: Aug 30 08:51AM +0200


> NO, that's a mess. Exceptions are by far more comfortable.
> The best variant are the distinction between checked and
> unchecked exceptions in Java.
 
And exceptions are more performant for the performance-relevant case
that no exception is thrown with table-driven excepion-handling. The
code-path that is processed when no exception is thrown is almost as
efficient as if there isn't any exception-handling (nearly zero-over-
head).
"Öö Tiib" <ootiib@hot.ee>: Aug 30 01:00AM -0700

On Friday, 30 August 2019 09:11:01 UTC+3, Mel wrote:
> > kind of fourth language that is neither C, C++ nor Rust.
 
> Rust generics are interface based. You cant do anything with
> generic type unless you tell which interfaces satisfies.
 
Perhaps I don't understand what you mean. Can you bring example?
The main point of generics seems to work in Rust about like in C++
templates. There are generic functions, data types and methods.
Instantiations of these work like concrete functions, data types and
methods.
It is more clear, efficient and/or type-safe than what can be done
in C.
Type erasure through "void*" or "..." function parameters is type-
unsafe and can be inefficient. Code generated with preprocessor
can be type safe and efficient but might have unneeded verbosity
in interface or to be hard to debug. So it is often better to have
copy-paste code in C.
Mel <mel@zzzzz.com>: Aug 30 10:13AM +0200

On Fri, 30 Aug 2019 08:51:21 +0200, Bonita Montero
> that no exception is thrown with table-driven excepion-handling. The
> code-path that is processed when no exception is thrown is almost as
> efficient as if there isn't any exception-handling (nearly
zero-over-
> head).
 
Exceptions take space or time, there is no escape, and they are non
deterministic...
 
--
Press any key to continue or any other to quit
David Brown <david.brown@hesbynett.no>: Aug 30 10:14AM +0200

On 29/08/2019 22:29, Keith Thompson wrote:
 
> What's terrible about it? Rust doesn't particularly need pointer
> arithmetic; unlike C, it's array indexing isn't defined in terms of
> pointers.
 
(Again, let me note that I know little about Rust - I've read some
stuff, but never used it. So if I get things wrong about it, I hope to
be corrected.)
 
As you say in your other post, pointer arithmetic isn't actually used
that much in C or C++, once you discount array access and iterators
which would be handled in a more "high level" manner in a non-C language.
 
But sometimes - even if it is only occasionally - it /is/ useful. This
can be especially true in low-level and systems code, which apparently
is a target for Rust.
 
The whole idea of Rust is to be "safer" than C - it aims to turn logical
errors such as misuse of pointers, confusion about owners, etc., into
compiler errors. That is a great ambition. But to work properly, you
have to follow the rules - workarounds that go outside those rules
should be as limited as possible, and strongly discouraged if they can't
be blocked by tools.
 
The language seems to be saying "We guarantee your code will avoid null
pointer dereferences, buffer overflows, use after free, memory leaks,
and all the pointer-related problems that plague C programmers." So
far, so good. Then it says "We realise that is too limiting sometimes,
so here are the ways to cheat, hack, and mess about under the hood".
Your guarantees are out the window, and you are back to /exactly/ the
same situation as you have in C - design carefully, code carefully to
keep your code safe, because the language and tools won't do it for you.
 
 
So it is not so much the idea of doing pointer arithmetic manually as
integer arithmetic that concerns me (though it might be a step lower
level than C's pointer arithmetic). It is the idea that the language
allows it and that it is considered an acceptable solution that bothers me.
Mel <mel@zzzzz.com>: Aug 30 10:16AM +0200

On Fri, 30 Aug 2019 01:00:48 -0700 (PDT), Öö Tiib<ootiib@hot.ee>
wrote:
> > generic type unless you tell which interfaces satisfies.
 
 
 
 
> Perhaps I don't understand what you mean. Can you bring example?
> The main point of generics seems to work in Rust about like in C++
 
Not even close. Imagin that you have to cite what abstract base
classes tvpe inherits, and only thing you can do with type is trough
interface...
 
--
Press any key to continue or any other to quit
Bonita Montero <Bonita.Montero@gmail.com>: Aug 30 10:18AM +0200

>> head).
 
> Exceptions take space or time, there is no escape, and they are non
> deterministic...
 
Computers are always deterministic. ;-)
But trowing exceptions is slow. But the case when we have a resouce-
or I/O-collapse isn't performance-relevant, i.e. it doesn't matter
if throwsing an exception lasts 10 or 1.000 clock-cycles.
"Öö Tiib" <ootiib@hot.ee>: Aug 30 01:26AM -0700

On Friday, 30 August 2019 11:16:32 UTC+3, Mel wrote:
 
> Not even close. Imagin that you have to cite what abstract base
> classes tvpe inherits, and only thing you can do with type is trough
> interface...
 
The examples in documentation ...
<https://doc.rust-lang.org/book/ch10-00-generics.html>
... are like that:
 
// generic type
struct Point<T> {
x: T,
y: T,
}
// generic method of generic type
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
// usage
fn main() {
let p = Point { x: 5, y: 10 };
 
println!("p.x = {}", p.x());
}
 
That does not seem to fit with what you describe.
Mel <mel@zzzzz.com>: Aug 30 10:31AM +0200

On Fri, 30 Aug 2019 10:18:02 +0200, Bonita Montero
> >> And exceptions are more performant for the performance-relevant
case
> >> that no exception is thrown with table-driven excepion-handling.
The
> >> code-path that is processed when no exception is thrown is
almost as
> > zero-over-
> >> head).
 
 
> > Exceptions take space or time, there is no escape, and they are
non
> But trowing exceptions is slow. But the case when we have a resouce-
> or I/O-collapse isn't performance-relevant, i.e. it doesn't matter
> if throwsing an exception lasts 10 or 1.000 clock-cycles.
 
I am not talking when you throw.
 
--
Press any key to continue or any other to quit
Mel <mel@zzzzz.com>: Aug 30 10:33AM +0200

On Fri, 30 Aug 2019 01:26:49 -0700 (PDT), Öö Tiib<ootiib@hot.ee>
wrote:
> > > > generic type unless you tell which interfaces satisfies.
 
> > > Perhaps I don't understand what you mean. Can you bring example?
> > > The main point of generics seems to work in Rust about like in
C++
 
> > Not even close. Imagin that you have to cite what abstract base
> > classes tvpe inherits, and only thing you can do with type is
trough
 
> println!("p.x = {}", p.x());
> }
 
 
> That does not seem to fit with what you describe.
 
Try to do something with x...
 
--
Press any key to continue or any other to quit
Bonita Montero <Bonita.Montero@gmail.com>: Aug 30 10:39AM +0200

>> or I/O-collapse isn't performance-relevant, i.e. it doesn't matter
>> if throwsing an exception lasts 10 or 1.000 clock-cycles.
 
> I am not talking when you throw.
 
The non-throw-case takes almost no or no performance.
Evaluating return-codes is simply a magnitude slower.
Mel <mel@zzzzz.com>: Aug 30 10:43AM +0200

On Fri, 30 Aug 2019 10:39:19 +0200, Bonita Montero
> >> Computers are always deterministic. ;-)
> >> But trowing exceptions is slow. But the case when we have a
resouce-
> >> or I/O-collapse isn't performance-relevant, i.e. it doesn't
matter
> >> if throwsing an exception lasts 10 or 1.000 clock-cycles.
 
 
> > I am not talking when you throw.
 
 
> The non-throw-case takes almost no or no performance.
 
That's no true. You have to install handlers evrry time or prepare
tbla for each function at program start...
> Evaluating return-codes is simply a magnitude slower.
 
This is especially not true.
 
--
Press any key to continue or any other to quit
Bonita Montero <Bonita.Montero@gmail.com>: Aug 30 10:47AM +0200

>> Evaluating return-codes is simply a magnitude slower.
 
> This is especially not true.
 
No, this is true.
You don't simply unterstand the implications of table-driven
exception handling or table-driven exception-handling at all.
David Brown <david.brown@hesbynett.no>: Aug 30 11:41AM +0200

On 30/08/2019 10:47, Bonita Montero wrote:
>>> Evaluating return-codes is simply a magnitude slower.
 
>> This is especially not true.
 
> No, this is true.
 
I take it you have measured this yourself? You have sample code,
measurements, statistics to back up this very specific claim?
 
Or is it just based on "something you read somewhere", repeated in the
hope that people will believe you more the second time?
 
 
> You don't simply unterstand the implications of table-driven
> exception handling or table-driven exception-handling at all.
 
I expect Mel does understand it. I am less convinced /you/ do.
 
 
Different error handling techniques have their pros and cons. Anyone
claiming one method is faster, safer, clearer, easier than other methods
is guaranteed to be /wrong/. They will have different balances, and
there is no solution that is the "best" in all situations, or the best
in all ways.
 
Table-driven exception handling tends to be efficient at run-time when
exceptions are not thrown, when you are running on larger processors
(like x86), and when you have no issues with code space. That makes it
a good choice (from the efficiency viewpoint) on PC programming.
 
But you can also find that the possibility of exceptions passing through
functions can affect their efficiency, even if they don't throw or
catching anything directly - it can affect ordering and optimisation of
the code by the compiler. It can affect the re-use of stack slots as
the compiler has to track all possible exception points and be able to
unwind correctly.
 
On resource-constrainted systems, this kind of thing can lead to unwind
tables that are bigger than the actual code size - a horrible level of
inefficiency that is unacceptable in many cases.
 
And while table-driven exception handling does not need extra
instruction codes (just data tables) for functions that pass on
exceptions without handling them, you need plenty of extra code - and
run-time - when throwing the exception, or when catching it.
 
 
Error handling via return codes, on the other hand, uses minimal extra
code space in functions that raise or handle the errors - usually less
overhead than for throwing or catching exceptions. It is certainly
going to be much faster when errors do occur. But you have to have some
way of passing on errors in functions between the "thrower" and the
"catcher". That means more explicit source code, but more efficient
object code.
 
 
Each technique has, as I said, its pros and cons. And there are lots of
reasons why one might pick one method over the other beyond efficiency -
do you want your error handling clear and explicit, or automatic and
hidden? Which is easier to get right, and which is harder to get wrong?
 
 
But one thing we can be sure of - Sutter would not have started the
process of getting "Zero-overhead deterministic exceptions" into C++ if
current C++ exceptions were as ideal as suggested. And while Sutter's
ideas here may come to naught, and he can be as wrong as anyone else, I
am more inclined to listen to his thoughts than those of Bonita.
Bonita Montero <Bonita.Montero@gmail.com>: Aug 30 11:56AM +0200

> I expect Mel does understand it. I am less convinced /you/ do.
 
Disassemble the code that calls a fuction that might throw an exceptions
with an without enabled EH. The difference is only, that the optimiza-
tions around that call might be slightly weakened; that's all. But that
usually doesn't outweigh the overhead against manually evaluating
return-codes.
 
> Different error handling techniques have their pros and cons. Anyone
> claiming one method is faster, safer, clearer, easier than other methods
> is guaranteed to be /wrong/.
 
1. Table-driven EH is usually faster.
2. I didn't say that it is safer or clearer,
but it is much more convenient.
3. When you come to the excepion-handling mechanism like in Java
with checked and unchecked exceptions, you get the best solution.
But for compatibility-reasons with C this isn't possible in C++.
 
> exceptions are not thrown, when you are running on larger processors
> (like x86), and when you have no issues with code space. That makes it
> a good choice (from the efficiency viewpoint) on PC programming.
 
Almost right, but it doesn't depend on the performance of the CPU.
Even on small CPUs the performance of processing a resurce- or I/O
-collapse doesn't count.
 
> the code by the compiler. It can affect the re-use of stack slots as
> the compiler has to track all possible exception points and be able to
> unwind correctly.
 
The loss of optimization-opportunities with table-driven exception
-handling is very slight. And the cost is of course not so high as
handling return-codes manually.
 
> On resource-constrainted systems, this kind of thing can lead to unwind
> tables that are bigger than the actual code size - a horrible level of
> inefficiency that is unacceptable in many cases.
 
That's true, but then you wouldn't use C++.
 
> Error handling via return codes, on the other hand, uses minimal extra
> code space in functions that raise or handle the errors - usually less
> overhead than for throwing or catching exceptions.
 
1. This case isn't performance relevant.
2. Yo cannot say that this is always true. The thing is simply that
there might be a number of return-codes evaluated at each call-level
which might result in a higher total CPU-time.
David Brown <david.brown@hesbynett.no>: Aug 30 01:08PM +0200

On 30/08/2019 11:56, Bonita Montero wrote:
> tions around that call might be slightly weakened; that's all. But that
> usually doesn't outweigh the overhead against manually evaluating
> return-codes.
 
There is no doubt that exceptions are aimed at having minimal run-time
cost when they are not thrown. But it is important to realise that
/minimal/ is not /zero/.
 
>> claiming one method is faster, safer, clearer, easier than other methods
>> is guaranteed to be /wrong/.
 
> 1. Table-driven EH is usually faster.
 
Usually it is faster when exceptions are not thrown, but a lot slower
when they /are/ thrown. But "usually" is not "always". And for some
situations, it is the worst case performance - the speed when there is a
problem - that is critical.
 
I'm happy with a claim "Table driven exceptions are generally faster,
when there are no errors, than other handling techniques for typical
code on PC's". What I am /not/ happy with is "Exception handling is
faster than other error return techniques".
 
> 2. I didn't say that it is safer or clearer,
>    but it is much more convenient.
 
Fair enough - though convenience is also a subjective matter.
 
> 3. When you come to the excepion-handling mechanism like in Java
>    with checked and unchecked exceptions, you get the best solution.
>    But for compatibility-reasons with C this isn't possible in C++.
 
Checked exceptions - where the type of exceptions that a function may
throw or pass on is part of the signature - are safer, clearer and more
efficient. (Compilers can use tables or code branches, whichever is
most convenient). But they involve more explicit information, which
some people like and others dislike. And they can't pass through
functions compiled without knowledge of the exceptions - such as C
functions compiled by a C compiler.
 
 
> Almost right, but it doesn't depend on the performance of the CPU.
> Even on small CPUs the performance of processing a resurce- or I/O
> -collapse doesn't count.
 
Incorrect.
 
Performance under the worst case is often a critical feature of high
reliability systems. It is precisely because the performance exception
handling is difficult to predict or measure that exceptions are banned
in most such systems.
 
 
> The loss of optimization-opportunities with table-driven exception
> -handling is very slight. And the cost is of course not so high as
> handling return-codes manually.
 
That may often be the case, but I would be very wary of generalising too
much.
 
>> tables that are bigger than the actual code size - a horrible level of
>> inefficiency that is unacceptable in many cases.
 
> That's true, but then you wouldn't use C++.
 
Nonsense. There are many good reasons to use C++, and many features
that can be used, without leading to any inefficiency or bloat.
 
>> code space in functions that raise or handle the errors - usually less
>> overhead than for throwing or catching exceptions.
 
> 1. This case isn't performance relevant.
 
Often not - but you are wrong to generalise like that. And you can
equally well argue that since exception handling is often used in cases
where you are dealing with I/O, the performance cost of manual return
code checks is negligible and therefore irrelevant with such code.
 
> 2. Yo cannot say that this is always true. The thing is simply that
>    there might be a number of return-codes evaluated at each call-level
>    which might result in a higher total CPU-time.
 
Yes, I have already mentioned that return code checking usually involves
some handling at functions along the chain between the "thrower" and the
"catcher".
Paavo Helde <myfirstname@osa.pri.ee>: Aug 30 02:08PM +0300

On 30.08.2019 11:43, Mel wrote:
> for each function at program start...
 
>> Evaluating return-codes is simply a magnitude slower.
 
> This is especially not true.
 
Your information is out of date for a couple of decades. The current C++
compilers have implemented zero cost exceptions (in the no-throw path)
already long time ago.
James Kuyper <jameskuyper@alumni.caltech.edu>: Aug 29 08:14PM -0400

On 8/29/19 2:14 PM, Stefan Große Pawig wrote:
> found only the QMap<QString, QVariant> as baked-in return value and then
> assumed that this was the only map type that could be stored in a
> QVariant?
The key point is that the author of the code freely chose to declare the
part map as QMap<QString, QVariant>, where the QVariant was used
exclusively to hold integers. He also freely choose to declare the
message map as QMap<QString, QVariant>, where the QVariant was used to
store a part map. There was no code outside the code he wrote which gave
him any excuse for using QVariant for both purposes - no code outside of
what he wrote used QVariant to interface with the code he did wrote.
Yes, he did use toMap(), to extract the map from the QVariant, but he
was the one who wrote the code to put the map into the QVariant.
 
If he'd used QMap<QString, int> for the part map, and QMap<QSTring,
QMap<QString, int> > for the message map, it would all have worked fine
(except for the bug that I fixed), and he would have been able to
extract the map directly, rather than by calling toMap(). So why did he
use QVariant instead? I'm getting the impression that he used QVariant
without bothering to consider whether it was actually needed.
David Brown <david.brown@hesbynett.no>: Aug 30 08:28AM +0200

On 30/08/2019 02:14, James Kuyper wrote:
> extract the map directly, rather than by calling toMap(). So why did he
> use QVariant instead? I'm getting the impression that he used QVariant
> without bothering to consider whether it was actually needed.
 
I can imagine a few possible explanations. One is that, as I mentioned
earlier, either the code or the programmer comes from a background of
dynamically typed language - he may simply have felt comfortable with
variant types. Perhaps he was less used to complex types and found
"QMap<QString, QVariant>" to be simpler (in his eyes) than
"QMap<QSTring, QMap<QString, int> >".
 
For a long time, templates had a reputation for program size bloat in
C++. (In the failed "EC++" "Embedded C++" standard, templates were
banned for that reason. Though $DEITY only knows why namespaces were
banned too.) Some people were left with the impression that you should
avoid instantiating too many different template types, to reduce code
bloat and duplication. Maybe he thought re-using a single map type was
more efficient in program size than using two different types.
 
(I'm not suggesting that this is a /good/ reason, even if it were the
case that it would give smaller code - something I doubt.)
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: