Friday, July 24, 2020

Digest for comp.lang.c++@googlegroups.com - 22 updates in 3 topics

aotto1968 <aotto1968@t-online.de>: Jul 24 09:01PM +0200

Hi,
 
a C++ class can be created on the "stack" or on the "heap"
 
class A {
int test;
}
 
// stack
A myA();
 
// heap
A* myA = new A();
 
 
Question:
 
it is possible (with gcc) to find out if a instance was created on a
"stack" or on a "heap" *
 
 
;-)
Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Jul 24 08:29PM +0100

On 24/07/2020 20:01, aotto1968 wrote:
> Hi,
 
> a C++ class can be created on the "stack" or on the "heap"
 
I think you mean objects not classes.
 
> }
 
> // stack
> A myA();
 
This is a function declaration not an object definition.
 
> A* myA = new A();
 
> Question:
 
> it is possible (with gcc) to find out if a instance was created on a "stack" or on a "heap" *
 
Only by comparing address of object with the addresses of sentinal objects in automatic storage either side of the object of interest but doing things like this means you are Doing It Wrong (TM). Why do you care if an object is in automatic storage or in the freestore?
 
/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."
Barry Schwarz <schwarzb@delq.com>: Jul 24 12:32PM -0700

On Fri, 24 Jul 2020 21:01:42 +0200, aotto1968 <aotto1968@t-online.de>
wrote:
 
 
>Question:
 
>it is possible (with gcc) to find out if a instance was created on a
>"stack" or on a "heap" *
 
If you tell us why you need to know, we might be able to offer
something meaningful.
 
In your first example, myA is an object of type A. In your second
example, myA is a pointer to an object of type A. When you write
code, you should know the types of the variables you are using.
 
If A is not trivial, you might consider evaluating sizeof(myA). For a
pointer, the result should be 4 or 8. For an object, it should be
greater.
 
--
Remove del for email
"Öö Tiib" <ootiib@hot.ee>: Jul 24 12:41PM -0700

On Friday, 24 July 2020 22:32:56 UTC+3, Barry Schwarz wrote:
 
> If you tell us why you need to know, we might be able to offer
> something meaningful.
 
> In your first example, myA is an object of type A.
 
No it is clearly a function without parameters returning A by value.
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Jul 24 01:08PM -0700

> On Fri, 24 Jul 2020 21:01:42 +0200, aotto1968 <aotto1968@t-online.de>
> wrote:
>>a C++ class can be created on the "stack" or on the "heap"
 
An *object* can be created on the stack or on the heap. (Incidentally,
the standard doesn't use those terms.)
 
>>}
 
>>// stack
>>A myA();
 
This declares myA as a function. Drop the parentheses.
 
 
> If A is not trivial, you might consider evaluating sizeof(myA). For a
> pointer, the result should be 4 or 8. For an object, it should be
> greater.
 
There's no reason sizeof(myA) couldn't be the same as the size of a
pointer. sizeof is not, except in some restricted cases, a good way to
distinguish among types.
 
As far as I know, there's no reliable way to determine whether a given
address is the address of an object allocated on the stack or on the
heap. There may be implementation-specific methods.
 
It's typically not a good idea to (try to) write code that depends on
this information. I suspect there's an underlying problem that has a
better solution.
 
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */
"Öö Tiib" <ootiib@hot.ee>: Jul 24 01:11PM -0700

On Friday, 24 July 2020 22:01:53 UTC+3, aotto1968 wrote:
> Hi,
 
> a C++ class can be created on the "stack" or on the "heap"
 
Life is not even near to that simple in C++. There is dynamic
storage in C++ that default operator news are providing and it
is close to what is often meant by "global heap". But user may
replace operator news and then new is providing storage what
user's version is providing.
<https://en.cppreference.com/w/cpp/memory/new/operator_new>
 
Rest of it is also way more fun than just "stack".
There is static storage that is global, there is thread-local
storage that is thread-specific, there is unspecified global
storage where objects of thrown exceptions reside and there is
automatic storage that is certainly not guaranteed to be in
(thread-specific) stack but most often is.
 
<erasing defective code>
 
> Question:
 
> it is possible (with gcc) to find out if a instance was created on a
> "stack" or on a "heap" *
 
This question is unanswerable as it is based on incorrectly simplified
concept of the classifications of kinds of memory in C++ programs.
Vir Campestris <vir.campestris@invalid.invalid>: Jul 24 09:55PM +0100

On 24/07/2020 20:29, Mr Flibble wrote:
> objects in automatic storage either side of the object of interest but
> doing things like this means you are Doing It Wrong (TM).  Why do you
> care if an object is in automatic storage or in the freestore?
 
Sentinel objects won't help. There's no reason why the stacks for
threads shouldn't be allocated on the heap - in fact I'd be surprised if
they aren't.
 
Andy
Paavo Helde <eesnimi@osa.pri.ee>: Jul 25 12:18AM +0300

24.07.2020 22:01 aotto1968 kirjutas:
> it is possible (with gcc) to find out if a instance was created on a
> "stack" or on a "heap" *
 
Only heuristically, and only in "nearby" stack, unless you have recorded
this information in the object itself when creating it.
 
A much more important question is why do you need this information? Most
probably you want to be too clever for no good. E.g. there are reasons
why std::bad_weak_ptr exception is thrown from shared_from_this if the
original object is not managed by a std::shared_otr. Suggesting not to
mess with stack/heap detection until you have understood those reasons.
Juha Nieminen <nospam@thanks.invalid>: Jul 24 10:12PM

> It's typically not a good idea to (try to) write code that depends on
> this information. I suspect there's an underlying problem that has a
> better solution.
 
Maybe he just asked out of curiosity rather than need.
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Jul 24 03:27PM -0700

>> "stack" or on a "heap" *
 
> This question is unanswerable as it is based on incorrectly simplified
> concept of the classifications of kinds of memory in C++ programs.
 
A more answerable question, similar to what the OP asked, might be:
 
Is it possible, given a pointer value, to determine whether the object
it points to has static, thread, automatic, or dynamic storage duration?
 
I think the answer is basically "no".
 
For a given implementation, it's likely to be possible to get *some*
information. For example, on my system an object allocated with
"new" has an address that's displayed as "0x5567e9909eb0", while a
local object has an address that's diplayed as "0x7ffcaee7786c".
That's probably good enough to distinguish between them *for
debugging purposes*. But making a program's behavior depend on
such a distinction is likely to be a bad idea.
 
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */
Daniel P <danielaparker@gmail.com>: Jul 24 03:46PM -0700

On Friday, July 24, 2020 at 3:29:16 PM UTC-4, Mr Flibble wrote:
> On 24/07/2020 20:01, aotto1968 wrote:
 
> > a C++ class can be created on the "stack" or on the "heap"
 
> I think you mean objects not classes.
 
If A is a class, "a A" is widely used as short for "an object of type A" (in
C++) or an instance of class A (elsewhere.) It's a concession to readability
with little danger of being misunderstood. Herb Sutter talks about "a
std::vector" this way, as does Scott Meyers.
 
Daniel
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Jul 24 03:50PM -0700

> C++) or an instance of class A (elsewhere.) It's a concession to readability
> with little danger of being misunderstood. Herb Sutter talks about "a
> std::vector" this way, as does Scott Meyers.
 
Informally Talking about "a std::vector" to mean "an object of type
std::vector" is fine.
 
Referring to an object as "a class", in my opinion, is not.
 
Given
std::vector<int> v;
std::vector is a class.
std::vector is a type.
v is a std::vector.
v is an object.
v is not a class.
 
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */
Daniel P <danielaparker@gmail.com>: Jul 24 04:01PM -0700

On Friday, July 24, 2020 at 6:50:33 PM UTC-4, Keith Thompson wrote:
> v is a std::vector.
> v is an object.
> v is not a class.
 
Point taken :-)
 
Daniel
Juha Nieminen <nospam@thanks.invalid>: Jul 24 05:51AM

> C++ already had two byte types too many - char, signed char,
> and unsigned char.
 
I don't know what the motivation was originally to have three distinct char
types in C (from which C++ "inherited" them), but in a way it actually
makes sense, perhaps serendipitously.
 
The type 'char' is supposed to be the most efficient byte type for the
target platform, either a signed one or an unsigned one. Most usually
it should be preferred especially when dealing with strings, as long
as one is aware that it can be either signed or unsigned, and doesn't
make any assumptions either way.
 
Curiously, this is not just theoretical. There is a *modern* very
concrete example where this has bitten many a developer in the posterior,
with code compiling but working incorrectly, because it wrongly assumes
that 'char' is signed.
 
Namely in ARM processors (at least the 32-bit ones) it so happens that
an unsigned char is more efficient than a signed one, and thus most
compilers (such as gcc) will use the 'char' type as an unsigned char.
Most notoriously this happens when compiling for the Raspberry Pi (and
probably other ARM-based systems).
 
Many a C and C++ program out there doesn't work correctly for the Raspi
because it wrongly assumes that 'char' is signed. I have encountered
actual examples.
 
(Most often this happens because of a if(c < 0), which obviously
always evaluates to false if char is unsigned.)
"Öö Tiib" <ootiib@hot.ee>: Jul 23 11:29PM -0700

On Thursday, 23 July 2020 17:07:43 UTC+3, Daniel P wrote:
 
> It has to be
 
> std::vector<std::byte> v = {std::byte{0x66},std::byte{0x6f},
> std::byte{0x6f}};
 
Yes and there is uint8_t that is in certain esoteric way better
than all of those others.
 
Also writing your own custom constexpr literal operator for std::byte
will take considerable effort of yours. Plus if you have large fields
of such binary data then also make compiling it slow. Plus if someone
looks its code of it then they likely go ewwwww. Especially if the
literal operator supports all the forms like:
 
constexpr auto h = 0x66_b;
constexpr auto d = 102_b;
constexpr auto o = 0146_b:
constexpr auto b = 0b1100110_b;
static_assert(h == d and d == o and o == b);
 
The idea of making raw bytes safer was actually good but it resulted
with fifth inconvenient to use thing with bear traps attached.
David Brown <david.brown@hesbynett.no>: Jul 24 08:45AM +0200

On 24/07/2020 07:51, Juha Nieminen wrote:
> compilers (such as gcc) will use the 'char' type as an unsigned char.
> Most notoriously this happens when compiling for the Raspberry Pi (and
> probably other ARM-based systems).
 
On most embedded targets, and most newer ABI's, plain char is unsigned
because making "char" signed is a totally meaningless historical
artefact from the days of 7-bit ASCII as the the only character set
supported by C. The signedness of plain char is specified in the ABI,
not given by the compiler - and pretty much every target except 32-bit
Windows and a few embedded microcontrollers has a proper ABI that pretty
much every compiler follows. (Though gcc, and some other compilers, may
let you override the signedness of char with a command-line switch.)
 
> actual examples.
 
> (Most often this happens because of a if(c < 0), which obviously
> always evaluates to false if char is unsigned.)
 
Any code that makes any assumptions about the signedness of plain char
is broken. If the signedness matters, make it explicit. (Usually
int8_t and uint8_t make vastly more sense in code than "signed char" and
"unsigned char". Or int_least8_t and uint_least8_t for maximal
portability.)
 
Unfortunately, you are right that people sometimes write such broken code.
David Brown <david.brown@hesbynett.no>: Jul 24 08:48AM +0200

On 23/07/2020 16:07, Daniel P wrote:
 
> It has to be
 
> std::vector<std::byte> v = {std::byte{0x66},std::byte{0x6f},
> std::byte{0x6f}};
 
Why? I must be missing something here, besides my morning coffee.
Bo Persson <bo@bo-persson.se>: Jul 24 09:52AM +0200

On 2020-07-24 at 07:51, Juha Nieminen wrote:
> it should be preferred especially when dealing with strings, as long
> as one is aware that it can be either signed or unsigned, and doesn't
> make any assumptions either way.
 
But people did. :-)
 
Dennis Ritchie writes in a C history paper about char being unspecified,
but just happened to be signed on their first implementations.
 
However, as soon as they did their first unsigned version (for a new
machine) it became apparent that users had already used char as a small
signed integer. And now their programs didn't work. Bad compiler!
 
So he added signed char to enable them to write more portable programs.
 
Soon he had to also add unsigned char for users of the new machines, so
they could move their programs to the older systems.
 
 
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Jul 24 10:55AM -0700

Bo Persson <bo@bo-persson.se> writes:
[...]
 
> So he added signed char to enable them to write more portable programs.
 
> Soon he had to also add unsigned char for users of the new machines,
> so they could move their programs to the older systems.
 
Hmm. I thought that unsigned char was added before signed char was.
 
In K&R1 (1978), "unsigned" could only be applied to int, and the
"signed" keyword didn't exist. K&R2 has the full modern set of integer
types (other than long long).
 
signed char isn't mentioned here:
https://www.bell-labs.com/usr/dmr/www/chist.html
 
[...]
 
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */
Keith Thompson <Keith.S.Thompson+u@gmail.com>: Jul 24 10:58AM -0700

> actual examples.
 
> (Most often this happens because of a if(c < 0), which obviously
> always evaluates to false if char is unsigned.)
 
One way this error can occur is if the program stores the result of
getchar() in a char object rather than an int.
 
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */
Juha Nieminen <nospam@thanks.invalid>: Jul 24 05:53AM

> If I was writing supremely-portable code that I know might make its way to a less-than-perfect compiler, I might do something like this.
 
> I found an alignment error in a PIC compiler a few years ago -- compilers aren't perfect.
 
I don't think one should program trying to take into account hypothetical bugs
in some random compiler. That doesn't make much sense.
David Brown <david.brown@hesbynett.no>: Jul 24 10:32AM +0200

On 24/07/2020 07:53, Juha Nieminen wrote:
 
>> I found an alignment error in a PIC compiler a few years ago -- compilers aren't perfect.
 
> I don't think one should program trying to take into account hypothetical bugs
> in some random compiler. That doesn't make much sense.
 
No, you'd never get anything written. I once used a compiler (also for
the PIC, which is a seriously brain-dead 8-bit microcontroller) that
could handle structs, and arrays, but not arrays of structs or structs
with arrays in them. You don't write code with such limitations unless
you /really/ need to.
 
There are occasional non-conformities that are not bugs, but active
choices by compiler manufacturers, and portable code can therefore take
them into consideration if the code needs to run on such systems. TI
has a habit on their microcontroller tools of not zeroing uninitialised
statically allocated data. The justification is that it can mean the
time from microcontroller reset to the start of "main" is too long and
can cause problems with watchdogs. This is nonsense, in my experience,
and this misfeature is the cause of countless problems. Some portable
embedded C code explicitly zero-initialises data in order to avoid
problems if you use it with a TI compiler. (Another non-conforming
"feature" that has a lot more justification is that for small 8-bit
microcontrollers, "double" is often implemented as the same size as
32-bit float.)
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: