POV-Ray : Newsgroups : povray.off-topic : C++: Seriously? Server Time
28 Jul 2024 18:20:39 EDT (-0400)
  C++: Seriously? (Message 1 to 9 of 9)  
From: Orchid Win7 v1
Subject: C++: Seriously?
Date: 21 Nov 2013 14:57:00
Message: <528e658c$1@news.povray.org>
Today, something blew my mind.

I was surfing the net, and I happened to wonder about how best ensure 
stuff gets freed while coding in C++. So I went to Stack Overflow. There 
was an accepted answer about RAII - but right below it, with 5x the 
up-votes, was an answer that basically said

   Everybody says RAII, and that's a good answer. But there's a better 
one: DON'T allocate dynamic memory in the first place! That is, rather 
than writing

   Foo * foo = new Foo();

just write

   Foo foo();

Reflecting on this for a moment, I saw the wisdom of it. If your stuff 
isn't dynamic, you can never ever forget to free it, leave it dangling, 
or have any of the other problems associated with manual memory 
allocation. (About the worst thing you can do is give somebody else a 
pointer to it - don't do that!)

But then I wondered... how can I apply that here?

   char * buffer = new char[1024];
   stream.read(buffer, 1024);
   ...stuff...
   delete[] buffer;

The thing that actually confused me was what the syntax for creating a 
non-dynamic array is - I've never seen anybody do that before. Ever. 
(Not in C or C++, anyway.) It turns out the answer is pretty trivial:

   char buffer[1024];
   stream.read(buffer, 1024);

Now you don't have to *care* that the function might have fifteen return 
statements, or that stuff might throw exceptions... your memory will 
*always* be deallocated when you leave. Because that's how stacks work.

But then somebody else pointed out that this is find for a 1KB buffer, 
but probably a bad idea for a 10MB one. (It also fails if you don't 
statically know how much data you want - which is pretty much the 
*original* reason for inventing dynamic allocation in the first place...)

The suggested solution? "Why don't you just use a std::vector<char>?"

Erm... because std::vector<char> /= char *. One is basically an 
unstructured chunk of arbitrary memory, this other is a complex 
implementation-dependent data structure.

"What's wrong with using vector.front()?"

...

My mind is blown.

OK, so the documentation says that vector.front() gives you a reference 
to the first element of the vector. Nothing surprising there. However, 
if you try to pass that to stream.read()... Woah, that's a whole *bunch* 
of assumptions, right there! o_O

The documentation guarantees that what gets returned is a reference to 
the first element. Firstly, I wasn't actually aware you could even use a 
reference as a pointer; I thought they were separate concepts. But 
secondly, I had assumed that the actual location of vector elements was 
implementation-defined; they might be in a tree, a linked list, a table 
or tables... anything!

But this code... this crazy code seems to be *assuming* that all the 
elements always just happen to be perfectly contiguous in memory.

I'm still trying to figure out whether this audacious assumption about 
the internal behaviour of a library class is more or less evil than just 
trying to do manual memory management correctly...

   Manual memory management? Pretty evil.

   Relying on internal library implementation details? Pretty evil.

Maybe the set of coding evilness is only partially ordered...


Post a reply to this message

From: Le Forgeron
Subject: Re: C++: Seriously?
Date: 21 Nov 2013 15:12:30
Message: <528e692e@news.povray.org>
Le 21/11/2013 20:57, Orchid Win7 v1 nous fit lire :

> "What's wrong with using vector.front()?"

> But this code... this crazy code seems to be *assuming* that all the
> elements always just happen to be perfectly contiguous in memory.
> 

all is in the specification... C++, but which version ?

For instance, [] on std::string is unspecified for complexity in C++98.
In C++11, it is constant in complexity.

which mean a loop with an index to access all element of a string is:
* dangerous in C++98, for performance
* fine in C++11.

From specification of std::vector, the elements are contiguous in term
of storage.

> I'm still trying to figure out whether this audacious assumption about
> the internal behaviour of a library class is more or less evil than just
> trying to do manual memory management correctly...

There is the concepts... vector, string, map...
and there is the specifications.

your best friend is www.cplusplus.com/reference, and the search box.

> 
>   Manual memory management? Pretty evil.
> 
>   Relying on internal library implementation details? Pretty evil.

If if remains unspecified, it is evil, but READ THE SPECS!


> 
> Maybe the set of coding evilness is only partially ordered...


Post a reply to this message

From: clipka
Subject: Re: C++: Seriously?
Date: 21 Nov 2013 15:17:37
Message: <528e6a61$1@news.povray.org>
Am 21.11.2013 20:57, schrieb Orchid Win7 v1:
> Today, something blew my mind.
>
> I was surfing the net, and I happened to wonder about how best ensure
> stuff gets freed while coding in C++. So I went to Stack Overflow. There
> was an accepted answer about RAII - but right below it, with 5x the
> up-votes, was an answer that basically said
>
>    Everybody says RAII, and that's a good answer. But there's a better
> one: DON'T allocate dynamic memory in the first place! That is, rather
> than writing
>
>    Foo * foo = new Foo();
>
> just write
>
>    Foo foo();
>
> Reflecting on this for a moment, I saw the wisdom of it. If your stuff
> isn't dynamic, you can never ever forget to free it, leave it dangling,
> or have any of the other problems associated with manual memory
> allocation. (About the worst thing you can do is give somebody else a
> pointer to it - don't do that!)
>
> But then I wondered... how can I apply that here?
>
>    char * buffer = new char[1024];
>    stream.read(buffer, 1024);
>    ...stuff...
>    delete[] buffer;
>
> The thing that actually confused me was what the syntax for creating a
> non-dynamic array is - I've never seen anybody do that before. Ever.
> (Not in C or C++, anyway.) It turns out the answer is pretty trivial:
>
>    char buffer[1024];
>    stream.read(buffer, 1024);
>
> Now you don't have to *care* that the function might have fifteen return
> statements, or that stuff might throw exceptions... your memory will
> *always* be deallocated when you leave. Because that's how stacks work.
>
> But then somebody else pointed out that this is find for a 1KB buffer,
> but probably a bad idea for a 10MB one. (It also fails if you don't
> statically know how much data you want - which is pretty much the
> *original* reason for inventing dynamic allocation in the first place...)
>
> The suggested solution? "Why don't you just use a std::vector<char>?"

Now /that/ is smart... avoiding allocation of dynamic memory by a class 
that /encapsulates/ dynamic memory allocation...

Anyone notice that this /is/ the RAII principle?!


So if we're using RAII anyway, what's wrong with

   {
     auto_ptr<char*> buffer = auto_ptr<char*>(new char[N]);
     stream.read(*buffer, N);
     //...stuff...
     //...oops, forgot to de-allocate... or did I?
   }

(except that you need to use either the tr1 library or boost)?


> I'm still trying to figure out whether this audacious assumption about
> the internal behaviour of a library class is more or less evil than just
> trying to do manual memory management correctly...
>
>    Manual memory management? Pretty evil.
>
>    Relying on internal library implementation details? Pretty evil.

Automatic memory management using smart pointers? Pretty smart.


Post a reply to this message

From: Warp
Subject: Re: C++: Seriously?
Date: 21 Nov 2013 16:15:21
Message: <528e77e9@news.povray.org>
Orchid Win7 v1 <voi### [at] devnull> wrote:
> The suggested solution? "Why don't you just use a std::vector<char>?"

> Erm... because std::vector<char> /= char *. One is basically an 
> unstructured chunk of arbitrary memory, this other is a complex 
> implementation-dependent data structure.

std::vector is not very implementation-dependent because the standard
guarantees certain properties for it. One of these properties is that
std::vector will always allocate contiguous memory (in the same way
as 'new[]' does.)

This means that if you get a pointer to the first element, then you
have effectively a pointer to an array of chars.

> "What's wrong with using vector.front()?"

The usual way to get a pointer to the first element is "&v[0]", but
I suppose "&v.front()" works too.

> OK, so the documentation says that vector.front() gives you a reference 
> to the first element of the vector. Nothing surprising there. However, 
> if you try to pass that to stream.read()... Woah, that's a whole *bunch* 
> of assumptions, right there! o_O

You shouldn't be passing a *reference* to the first element. You should
be passing a *pointer* to the first element (although this is probably
nitpicking.)

> The documentation guarantees that what gets returned is a reference to 
> the first element. Firstly, I wasn't actually aware you could even use a 
> reference as a pointer; I thought they were separate concepts.

You can't. You have to take a pointer to said element (using the
prefix-& operator.)

> But secondly, I had assumed that the actual location of vector elements was 
> implementation-defined; they might be in a tree, a linked list, a table 
> or tables... anything!

No, because the C++ standard guarantees that the memory allocated by
std::vector will always be contiguous, the same as 'new[]' does.

std::vector was specifically designed in the standard so that it could
be used for these kinds of things.

> But this code... this crazy code seems to be *assuming* that all the 
> elements always just happen to be perfectly contiguous in memory.

And that's perfectly standard.

>    Relying on internal library implementation details? Pretty evil.

Not when the standard guarantees the necessary properties.

-- 
                                                          - Warp


Post a reply to this message

From: Orchid Win7 v1
Subject: Re: C++: Seriously?
Date: 21 Nov 2013 17:19:52
Message: <528e8708$1@news.povray.org>
On 21/11/2013 09:15 PM, Warp wrote:
> Orchid Win7 v1<voi### [at] devnull>  wrote:
>> The suggested solution? "Why don't you just use a std::vector<char>?"
>
>> Erm... because std::vector<char>  /= char *. One is basically an
>> unstructured chunk of arbitrary memory, this other is a complex
>> implementation-dependent data structure.
>
> std::vector is not very implementation-dependent because the standard
> guarantees certain properties for it. One of these properties is that
> std::vector will always allocate contiguous memory (in the same way
> as 'new[]' does.)

The standard actually *promises* this? So there is literally only one 
possible implementation for vector?

Wow. My mind is blown...


Post a reply to this message

From: Le Forgeron
Subject: Re: C++: Seriously?
Date: 22 Nov 2013 05:13:10
Message: <528f2e36@news.povray.org>
Le 21/11/2013 23:20, Orchid Win7 v1 a écrit :
> On 21/11/2013 09:15 PM, Warp wrote:
>> Orchid Win7 v1<voi### [at] devnull>  wrote:
>>> The suggested solution? "Why don't you just use a std::vector<char>?"
>>
>>> Erm... because std::vector<char>  /= char *. One is basically an
>>> unstructured chunk of arbitrary memory, this other is a complex
>>> implementation-dependent data structure.
>>
>> std::vector is not very implementation-dependent because the standard
>> guarantees certain properties for it. One of these properties is that
>> std::vector will always allocate contiguous memory (in the same way
>> as 'new[]' does.)
> 
> The standard actually *promises* this? So there is literally only one
> possible implementation for vector?
> 
> Wow. My mind is blown...

It's not a promise, it's a contract.
you can make your own std::vector (well, if you dare to provide your own
compiler...), but you can only claims conformance to standard if your
implementation provides all the tiny expectation that are in the
standard. The standard describes the minimal requirements, you can
provide more than the bare minimum.

And if you are not conform, you should not name it std::vector.

If it is named spoon and works like a spoon, it's a spoon and everybody
will be ok.
If you name it spoon and it works like a fork without being able to be
use as spoon in all aspect, you are putting shit on everyone.
If you name it spoon and it can work as well as a spoon or a fork, it's ok.

-- 
Just because nobody complains does not mean all parachutes are perfect.


Post a reply to this message

From: Warp
Subject: Re: C++: Seriously?
Date: 22 Nov 2013 11:32:43
Message: <528f872b@news.povray.org>
Orchid Win7 v1 <voi### [at] devnull> wrote:
> So there is literally only one possible implementation for vector?

Not really. For example, the standard doesn't make guarantees about
when exactly reallocation happens when you keep adding elements to
the vector, or how much the capacity is increased each time it happens.
That's up to the implementation. (The only thing that the standard
guarantees is that adding new elements at the end of the vector is
amortized-constant-time.)

It also doesn't give promises about where the allocated memory will be.
(For example, it would be perfectly possible for a std::vector
implementation to use a "short vector optimization", which would be
similar to the concept of "short string optimization". All this would
be completely transparent to the outside.)

The only thing it does guarantee is that the memory allocated by
std::vector will be contiguous (like with 'new[]'), and a pointer
to one of its elements will work the same as a pointer to an element
in a raw array.

(Note that if you add a new element to a std::vector, any existing
iterators and pointers to it are invalidated. This is because if
reallocation happens, they will end up pointing to freed memory,
and thus using them after that is a no-no.)

-- 
                                                          - Warp


Post a reply to this message

From: Orchid Win7 v1
Subject: Re: C++: Seriously?
Date: 22 Nov 2013 14:03:04
Message: <528faa68$1@news.povray.org>
On 22/11/2013 04:32 PM, Warp wrote:
> Orchid Win7 v1<voi### [at] devnull>  wrote:
>> So there is literally only one possible implementation for vector?
>
> Not really. For example, the standard doesn't make guarantees about
> when exactly reallocation happens when you keep adding elements to
> the vector, or how much the capacity is increased each time it happens.
> That's up to the implementation. (The only thing that the standard
> guarantees is that adding new elements at the end of the vector is
> amortized-constant-time.)
>
> It also doesn't give promises about where the allocated memory will be.
> (For example, it would be perfectly possible for a std::vector
> implementation to use a "short vector optimization", which would be
> similar to the concept of "short string optimization". All this would
> be completely transparent to the outside.)
>
> The only thing it does guarantee is that the memory allocated by
> std::vector will be contiguous (like with 'new[]'), and a pointer
> to one of its elements will work the same as a pointer to an element
> in a raw array.
>
> (Note that if you add a new element to a std::vector, any existing
> iterators and pointers to it are invalidated. This is because if
> reallocation happens, they will end up pointing to freed memory,
> and thus using them after that is a no-no.)

That makes sense, I guess...

(One helpful commenter pointed out that std::vector<bool> is allowed to 
behave differently, though.)

I did enjoy the guy who claimed that because you're guaranteed O(1) 
access time, it must be a continuous array. O RLY? Because I'm pretty 
sure I can come up with an O(1) implementation that isn't contiguous...


Post a reply to this message

From: Warp
Subject: Re: C++: Seriously?
Date: 22 Nov 2013 16:30:13
Message: <528fcce5@news.povray.org>
Orchid Win7 v1 <voi### [at] devnull> wrote:
> (One helpful commenter pointed out that std::vector<bool> is allowed to 
> behave differently, though.)

std::vector<bool> is special in that each element takes one bit.
You can't have a pointer pointing to a single bit. (std::vector<bool>
iterators are also special.)

> I did enjoy the guy who claimed that because you're guaranteed O(1) 
> access time, it must be a continuous array. O RLY? Because I'm pretty 
> sure I can come up with an O(1) implementation that isn't contiguous...

std::deque has O(1) random access and O(1) insertion and deletion from
the beginning and the end, and it doesn't use contiguous memory for the
entire data structure. (Iterators also remain valid even if you add
elements.)

On the other hand, the O(1) random access is slightly slower than with
std::vector.

-- 
                                                          - Warp


Post a reply to this message

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.