Wednesday, August 14, 2019

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

Tim Rentsch <tr.17687@z991.linuxsc.com>: Aug 14 09:27AM -0700

> fail / trap an unaligned access at runtime and the OS might emulate
> tzhe unaligned access or not. So it depends on the platform you
> target.
 
If a given operation has undefined behavior, it doesn't stop
having undefined behavior just because it happens to do what
you want it to.
Bonita Montero <Bonita.Montero@gmail.com>: Aug 14 08:51PM +0200


> If a given operation has undefined behavior, it doesn't stop
> having undefined behavior just because it happens to do what
> you want it to.
 
In theory, but in practice it has an expectable behaviour depending
on the platform.
"Öö Tiib" <ootiib@hot.ee>: Aug 14 01:14PM -0700

On Wednesday, 14 August 2019 21:51:32 UTC+3, Bonita Montero wrote:
> > you want it to.
 
> In theory, but in practice it has an expectable behaviour depending
> on the platform.
 
So it may change because of change of compiler, its version, compiling
options, device that it is ran on, settings of operating system or
other (possibly unrelated) code in same code-base.
Program that happens to do something in narrow conditions but not in
others is unreliable and unreliability is the most disliked property
of software.
Melzzzzz <Melzzzzz@zzzzz.com>: Aug 14 12:37AM


>> throw new exception();
 
> Aside from what everyone else has said...
 
> Because C++ isn't Java.
 
Except if you don't work with MFC ;)
 
--
press any key to continue or any other to quit...
U ničemu ja ne uživam kao u svom statusu INVALIDA -- Zli Zec
Na divljem zapadu i nije bilo tako puno nasilja, upravo zato jer su svi
bili naoruzani. -- Mladen Gogala
Jivanmukta <jivanmukta@poczta.onet.pl>: Aug 14 10:06AM +0200

W dniu 13.08.2019 o 17:02, Bo Persson pisze:
 
> In the latter case you have to catch a pointer and also remember to
> delete it somewhere. Extra work. And an extra allocation.
 
> So most people prefer the first version.
 
Do you mean that in the line:
 
throw exception();
 
I create automatic object of a class exception?
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Aug 14 10:25AM +0200

On 14.08.2019 10:06, Jivanmukta wrote:
 
> Do you mean that in the line:
 
> throw exception();
 
> I create automatic object of a class exception?
 
You're creating a temporary, that's copied/moved somewhere.
 
Throwing is anyway a high cost operation. Whether it involves dynamic
allocation internally depends on the C++ implementation.
 
Your responsibility ends with the creation of the temporary. Any memory
management after that is automatic. Of course, it relies on assumed
correctness of copy constructor and/or move constructor.
 
 
Cheers!,
 
- ALf
Jorgen Grahn <grahn+nntp@snipabacken.se>: Aug 14 10:18AM

On Wed, 2019-08-14, Jivanmukta wrote:
 
> Do you mean that in the line:
 
> throw exception();
 
> I create automatic object of a class exception?
 
Like someone else hinted, if this surprises you and you come from a
Java background, you should probably stop and read up on the
differences between the languages. C++ and Java are very different
in this area.
 
/Jorgen
 
--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
James Kuyper <jameskuyper@alumni.caltech.edu>: Aug 14 08:41AM -0400

On 8/14/19 4:25 AM, Alf P. Steinbach wrote:
 
> You're creating a temporary, that's copied/moved somewhere.
 
> Throwing is anyway a high cost operation. Whether it involves dynamic
> allocation internally depends on the C++ implementation.
 
True, but only in the same sense that it is equally true of passing an
argument to a function - an implementation is free to dynamically
allocate space for function parameters, so long as it does so in a way
that's transparent to the user, and the same is true of exception
objects. Any method that user code could use to detect the fact that
it's dynamically allocated (for instance, by trying to deallocate it)
has behavior not defined by the standard.
 
"The name declared in an exception-declaration is local to the
handler..." (6.3.3p4 "Block Scope").
"Block-scope variables not explicitly declared static, thread_local, or
extern have automatic storage duration. ..." (6.7.3p1).
Exception declarations are not allowed to have storage class specifiers
(18p1), so none of those exceptions apply.
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Aug 14 07:39PM +0200

On 14.08.2019 14:41, James Kuyper wrote:
>> allocation internally depends on the C++ implementation.
 
> True, but only in the same sense that it is equally true of passing an
> argument to a function
 
No, I didn't write a meaningless truism, even though I was a member of
the Tautology Club while I was a member of the Tautology Club. Sorry if
it gave the appearance of being babble.
 
From C++11 you can get what's functionally a shared pointer to an
exception object, via `std::current_exception`.
 
Whatever it refers to must necessarily be dynamically allocated, and an
implementation might, for simplicity, do that up front at the throw
point, rather than deferring the dynamic allocation in the hope that it
will not be needed and that copying the object will be cheaper.
 
 
[snip]
> Any method that user code could use to detect the fact that
> it's dynamically allocated (for instance, by trying to deallocate it)
> has behavior not defined by the standard.
 
There's no need to detect. With `current_exception` one knows it's
dynamically allocated. Deallocation is a matter of destroying all owning
smart pointers to that exception object. Which does not guarantee to
deallocate immediately, but ensures that proper cleanup is done.
 
 
> extern have automatic storage duration. ..." (6.7.3p1).
> Exception declarations are not allowed to have storage class specifiers
> (18p1), so none of those exceptions apply.
 
Yes, but it seems that here you went out on a tangent, into the land of
irrelevancies.
 
 
Cheers!,
 
- Alf
Tim Rentsch <tr.17687@z991.linuxsc.com>: Aug 14 09:26AM -0700


> int main( void )
> { pow( f( 0 ), f( 1 )); }
 
> For one example, the above might have undefined behavior,
 
No, it cannot.
 
> |to either a different side effect on the same scalar object
> |or a value computation using the value of the same scalar
> |object, the behavior is undefined.
 
None of that matters because function calls are (at a minimum)
indeterminately sequenced. The calls 'f(0)' and 'f(1)' cannot
overlap: one must be evaluated completely before the other, even
though we don't know which is done first and which is done second.
 
Even if function calls were not indeterminately sequenced, in C++
this example still would not have undefined behavior. Take this
simple example (disclaimer: not compiled):
 
#include <math.h>
#include <stdio.h>
 
int
main(){
int x = 0;
double d = pow( x, ++x );
printf( "d is %f\n", d );
return 0;
}
 
In C this program has undefined behavior. In C++ it does not,
because the evaluations of function arguments are indeterminately
sequenced with respect to each other. There is unspecified
behavior, but not undefined behavior.
Tim Rentsch <tr.17687@z991.linuxsc.com>: Aug 14 08:25AM -0700


> By the algorithm "invented by geniuses" for this particular case I was
> referring to Morris traversal, which walks a tree in in-order sequence
> with O(1) space and time. [...]
 
Of course you meant to say O(1) space and O(n) time.
Bonita Montero <Bonita.Montero@gmail.com>: Aug 14 09:59AM +0200

When I have a code like this:
 
#include <typeinfo>
 
struct S1
{
virtual void f();
};
 
struct S2 : public S1
{
virtual void f();
};
 
bool f( S1 *a, S2 *b )
{
return &typeid(a) == &typeid(b);
}
 
Is it really guaranteed to work, i.e. are the adresses
of typeid-objects really constants of all "invocations"
of the operator typeid()?
I know that there is type type_info::hash-code and it
could simply return a size_t which has the same value
as the address of the global type_info-object. But with
the implementation I checked I get a slower method-call.
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Aug 14 10:15AM +0200

On 14.08.2019 09:59, Bonita Montero wrote:
 
> Is it really guaranteed to work, i.e. are the adresses
> of typeid-objects really constants of all "invocations"
> of the operator typeid()?
 
As far as I know, no.
 
A portable way to compare two `std::type_info` objects for equality is
to just compare them directly with `==`, or `!=`.
 
The `before` method provides an ordering relation in an inconvenient
way. If you need ordering then consider converting the objects to
`std::type_index`, which provides a full set of relational operators.
 
 
> could simply return a size_t which has the same value
> as the address of the global type_info-object. But with
> the implementation I checked I get a slower method-call.
 
Not sure what you're asking, but I'd guess that for any implementation
`hash_code` is reasonably efficient while providing a reasonable code.
 
 
Cheers!,
 
- Alf
Bonita Montero <Bonita.Montero@gmail.com>: Aug 14 10:29AM +0200

> bool f( S1 *a, S2 *b )
> {
>     return &typeid(a) == &typeid(b);
return &typeid(*a) == &typeid(*b);
Bonita Montero <Bonita.Montero@gmail.com>: Aug 14 10:42AM +0200

> Not sure what you're asking, but I'd guess that for any implementation
> `hash_code` is reasonably efficient while providing a reasonable code.
 
Even doing typeid(x) is slow with VC++, and operator == also has a
lot of overhead; I just traced through the disassembly. It think
it's better to stick with a virtual method that gives a scalar value
to compare against other objects.
Bo Persson <bo@bo-persson.se>: Aug 14 11:54AM +0200

On 2019-08-14 at 10:42, Bonita Montero wrote:
> lot  of overhead; I just traced through the disassembly. It think
> it's better to stick with a virtual method that gives a scalar value
> to compare against other objects.
 
The "slowness" has to do with allowing the code to correctly compare two
typeid(x) possibly coming from different DLLs. That could easily produce
two different addresses, even if they are both constant.
 
The language standard doesn't say anything about dynamic libraries, so
this is an extension.
 
 
Bo Persson
"Öö Tiib" <ootiib@hot.ee>: Aug 14 04:56AM -0700

On Wednesday, 14 August 2019 11:29:34 UTC+3, Bonita Montero wrote:
> >     return &typeid(a) == &typeid(b);
> return &typeid(*a) == &typeid(*b);
> > }
 
Basically taking address of the const std​::​type_­info object from typeid is
wrong, since these are allowed to be different objects for same type:
 
const std::type_info& ti1 = typeid(A);
const std::type_info& ti2 = typeid(A);

assert(&ti1 == &ti2); // not guaranteed
assert(ti1.hash_code() == ti2.hash_code()); // guaranteed
assert(std::type_index(ti1) == std::type_index(ti2)); // guaranteed
Bonita Montero <Bonita.Montero@gmail.com>: Aug 14 03:19PM +0200

> The "slowness" has to do with allowing the code to correctly compare two
> typeid(x) possibly coming from different DLLs. That could easily produce
> two different addresses, even if they are both constant.
 
You're right. I just wrote a little EXE and DLL that tests this.
 
This is the shared header:
 
struct S
{
virtual void f();
};
 
void S::f()
{
}
 
This is the DLL (ignore the __declspec(...)):
 
#include <windows.h>
#include "..\shared\shared.h"
 
__declspec(dllexport)
void GimmeObject( S *&pS )
{
static S s;
pS = &s;
}
 
This is the executable:
 
#include <iostream>
#include "..\shared\shared.h"
 
using namespace std;
 
__declspec(dllimport)
void GimmeObject( S *&pS );
 
int main()
{
S s;
S *pS;
GimmeObject( pS );
cout << "typeid(s) == typeid(*pS): "
<< (typeid(s) == typeid(*pS) ? "true" : "false")
<< endl;
}
 
The whole Visual Studio 2019 project can be downloaded here:
https://transfer.sh/F4QKl/typeid_dll.zip
 
> The language standard doesn't say anything about dynamic libraries,
> so this is an extension.
 
Ok, and I think this also applies to dynamic_cast; i.e. you could
downcast an object built in a DLL to a class included in an EXE.
But I think it would have been cleverer to say that this doesn't
work across DLL / excecutable boundaries and let the VMT-pointers
and RTTI-information be a part that could be imported by indidual
compiler-directives. This would allow more performant implementa-
tions; f.e. type_id::hash_code() could be simply the address of
the VMT-table.
Bonita Montero <Bonita.Montero@gmail.com>: Aug 14 03:23PM +0200

> assert(&ti1 == &ti2); // not guaranteed
 
This is even slow with my MSVC++-implementation,
so it doesn't matter if this works or not. ;-)
Melzzzzz <Melzzzzz@zzzzz.com>: Aug 14 02:23PM


> Is it really guaranteed to work, i.e. are the adresses
> of typeid-objects really constants of all "invocations"
> of the operator typeid()?
 
Address perhaps changes, but you can take it and store it for later use.
 
 
 
--
press any key to continue or any other to quit...
U ničemu ja ne uživam kao u svom statusu INVALIDA -- 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>: Aug 14 04:45PM +0200

> Ok, and I think this also applies to dynamic_cast; i.e. you could
> downcast an object built in a DLL to a class included in an EXE.
 
Ok, it works:
 
shared.h (having the method doubled on the exe and dll ;-)):
 
struct S
{
virtual void f();
};
 
void S::f()
{
}
 
struct T : public S
{
virtual void f();
};
 
void T::f()
{
}
 
DLL:
 
#include <windows.h>
#include "..\shared\shared.h"
 
static S s;
static T t;
 
__declspec(dllexport)
void GimmeObject( S *&pS, S *&pT )
{
pS = &::s;
pT = &::t;
 
}
 
EXE:
 
#include <iostream>
#include "..\shared\shared.h"
 
using namespace std;
 
__declspec(dllimport)
void GimmeObject( S *&pS, S *&pT );
 
int main()
{
S s;
S *pS, *pT;
GimmeObject( pS, pT );
cout << "typeid(s) == typeid(*pS): "
<< (typeid(s) == typeid(*pS) ? "true" : "false")
<< endl;
cout << "dynamic_cast<T *>(pT) != nullptr: "
<< (dynamic_cast<T *>(pT) != nullptr ? "true" : "false")
<< endl;
}
ram@zedat.fu-berlin.de (Stefan Ram): Aug 14 08:34AM

>Do you mean that in the line:
>throw exception();
>I create automatic object of a class exception?
 
An object with automatic storage duration, which is then
copied.
 
In C++, one often creates such objects and copies them.
This is made quite efficient by many smart optimizations and
helps to avoid problems with memory management that might
occur when dynamic storage duration is used.
ram@zedat.fu-berlin.de (Stefan Ram): Aug 14 08:37AM

Supersedes: <automatic-20190814093254@ram.dialup.fu-berlin.de>
[removed claim about automatic storage duration]
 
>Do you mean that in the line:
>throw exception();
>I create automatic object of a class exception?
 
In C++, one often creates objects whose lifetime is a subset
of the lifetime of the current function execution and copies
them. This is made quite efficient by many smart optimizations
and helps to avoid problems with memory management that
might occur when dynamic storage duration is used.
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Aug 13 11:51PM -0700

On 8/13/2019 1:24 AM, Bonita Montero wrote:
> also lock the head-pointers. So if there is always a common´structure
> locked, I can stick with a single lock and I would get the same effi-
> ciency as your code.
 
Yeah. It would boil down to a single head.
 
For some reason I am thinking about a lock-free LIFO as the head.
Humm... Thinking. Working on some other stuff right now, but there seems
like a way to do this.
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Aug 13 11:55PM -0700

On 8/13/2019 11:51 PM, Chris M. Thomasson wrote:
>> locked, I can stick with a single lock and I would get the same effi-
>> ciency as your code.
 
> Yeah. It would boil down to a single head.
 
A page is touched, then instantly pushed onto the active lock-free LIFO.
Marked as logically removed, yet still exists. Can be moved later.
Humm... I have not worked on lock-free in a while. But might be able to
sketch out a strange thing on 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: