Placement new and exceptions
Asked Answered
C

4

5

The "placement new" operator is declared as such:

void* operator new (std::size_t size, void* ptr) noexcept;

But while it doesn't involve any actual allocation so bad allocation exceptions are eliminated, it is still possible that the pointer points to a bad location, in which case one would expect to get a range or over/underflow error, but won't the fact that it was declared as noexcept simply terminate execution instead?

Also does this mean prior to C++11 placement new will throw and try to handle an std::unexpected in case of std::set_unexpected instead of directly crashing?

Shouldn't there be a throwing overload of placement new "just in case"?

Cauda answered 2/11, 2014 at 12:32 Comment(12)
In case of what? It's your responsibility to make sure the address is valid, the default placement new doesn't even do anything, it just returns the address you provide.Carcinomatosis
This function is a no-op. It does not even try to write to *ptr. It is simply called by a new-expression, which always calls an allocation function - and this one is the no allocation function. Construction of the object is performed by the compiler evaluating the new-expression (outside of the call to this function).Garrott
@Garrott - most constructors access member variables, otherwise you will have an object with trivial, i.e. no constructor.Cauda
As I said (and tried to clarify in an edit): The constructor is not called from within this operator new function. It is called by the compiler after completion of this function, as a part of evaluating the new-expression.Garrott
@Garrott - so you mean placement new practically does nothing but return you back the same pointer you passed to it? Then what's the point of using it at all? Overhead?Cauda
I think I'll write that up as an answer, but this will take some time..Garrott
I haven't tried it, but I would have expected that if you pass a bad pointer to placement new, your program will probably segfault. That is the only thing I can reasonably see happening, and that is not a case that throws. Am I missing something?Midwife
@TimSeguine - I assumed segfault is preventable through exception handling.Cauda
@Carcinomatosis nope, and I am relatively certain that by the time you segfault you are in undefined behavior territory, so additionally your cat could get pregnant.Midwife
A new-expression calls an operator new function and then calls a constructor. In the placement new case that uses this operator new, operator new itself does absolutely nothing and cannot fail, but the new-expression can still be undefined behavior.Padrone
@TimSeguine wrong username :)Carcinomatosis
@Carcinomatosis I guess I trust autocomplete too much. With 5k on this site, one would think you'd have picked a new username by now. ;)Midwife
S
5

Point of using placement syntax is to achieve non standard allocation, so non standard freeing is typically required. Consequently action taken depends on the allocator used.

It's your job to provide correct address for placement new to work.

EDITED IN RESPONSE TO COMMENTS:-

#include <new>        // Must #include this to use "placement new"
#include "Fred.h"     // Declaration of class Fred

    void someCode()
    {
      char memory[sizeof(Fred)];     // Line #1     //Allocate enough memory.
      void* place = memory;          // Line #2     // There's no need for this.

      Fred* f = new(place) Fred();   // Line #3 (see "NOTE" below)
      // The pointers f and place will be equal

      ...
    }

NOTE: You are taking sole responsibility that the pointer you pass to the "placement new" operator points to a region of memory that is big enough and is properly aligned for the object type that you're creating. Neither the compiler nor the run-time system make any attempt to check whether you did this right. If your Fred class needs to be aligned on a 4 byte boundary but you supplied a location that isn't properly aligned, you can have a serious disaster on your hands.

In short this means you should be careful with the use of placement new OR if your a guy like me then never use it:)

Hope this would clear your doubts.

Sink answered 2/11, 2014 at 12:58 Comment(5)
My point is that writing to a void * can by no definition be considered a "non-failable" op.Cauda
If you are concerned about access violations, etc.. Those are things that don't exist in the standard.Falcongentle
"It's your job to provide correct address for placement new to work" one could argue that it is your job to write correct programs without bugs, so why do you need error handling in the first place :) That's why they call it "exceptions"Cauda
My point is will not always be as simple as "allocate space and pass to placement new" - that pointer might end up being the product of very complex logic that manages a bunch of memory pools with lots of code to get bugs from. Programming does not boil down to trivial examples.Cauda
"one could argue that it is your job to write correct programs without bugs" yes that's right/good but not necessary as is using placement new.Sink
G
3

To understand what this function does, I think it is necessary to take a look at what a new-expression does: It calls an allocation function to obtain storage for the object, then it constructs that object in the memory region the allocation function indicated (by returning a pointer to said memory region).

This implies that construction is never performed by the allocation function itself. The allocation function has the strange name operator new.

It is possible to supply additional parameters for the allocation function by using the placement-new syntax:

new int(5)        // non-placement form
new(1,2,3) int(5) // placement-form

However, placement-new typically refers to a very specific new-expression:

void* address = ...;
::new(address) int(5) // "the" placement-form

This form is intended to construct an object in an already allocated region of memory, i.e. it is intended to just call the constructor, but not perform any allocation.

No special case has been introduced in the core language for that case. Rather, a special allocation function has been added to the Standard Library:

void* operator new (std::size_t size, void* ptr) noexcept;

Being a no-op (return ptr;), it allows calling the constructor for an object explicitly, constructing at a given memory location. This function call can be eliminated by the compiler, so no overhead is introduced.

Garrott answered 2/11, 2014 at 13:42 Comment(1)
So basically it is just for the sake of syntax sugar to make it "cleaner" to call constructors on arbitrary memory address. At least "clean" according to the lowly standard committee standards...Cauda
F
0

Placement new exists to make possible explicit constructor calls targeted into arbitrary buffers (for custom allocators, debugging, etc...). That's about it.

You can write your own that validates its input.

For example: A class might require some kind of alignment, and you suspect some custom allocator of fudging this up. So, you give the class a different placement new and see what happens when said allocator uses it.

Falcongentle answered 2/11, 2014 at 12:49 Comment(4)
I agree the possibility to accidentally provide a bad pointer is slimmer than running out of memory, but it still exists nonetheless. Of course you could validate but even that may fail in large and complex scenarios.Cauda
Well, if by complex, you mean something is unscrupulously grabbing constructor pointers and calling them directly, then most certainly yes. My experience has been as above: I use it to detect alignment shenanigans, give myself a way to keep track of instances created by containers, etc...Falcongentle
I mean even without intending, bad stuff happens. That's the whole point of exceptions, that's why I find it odd to leave such a corner case as writing to a void * without the possibility to handle eventual exceptions. It is not like it is some operation that is guaranteed to not fail...Cauda
The typical non-debug placement new just returns its argument. If the constructor throws, the appropriate placement delete is called with the same argument (which is usually a no-op).Falcongentle
S
0

You seem to think that the function could somehow validate the pointer it gets passed.

It cannot. There is nothing in the language standard that allows that. Granted, checking for the nullptr case would be possible, but only marginally useful.

Alignment errors are strictly UB, so any implementation is free to do anything. (even just have it work: x86)

Checking whether the memory area is big enough especially does not make any sense with placement new, as it may be quite likely that the user code puts other stuff in that area also, and the compiler cant check that. Apart from that C and C++ nowhere offer the ability to check the size of an allocated region of mem.

Shaneka answered 2/11, 2014 at 14:11 Comment(4)
No I just incorrectly assumed the program can recover from a segfault by handling some exception.Cauda
@Cauda - guess it depends on where your code runs. MSVC with /EHa ... just do a catch(...) and you catch your segfault. Just not that great idea.Shaneka
Still preferable to a mystery crash.Cauda
@Cauda - Crashes are normally not "mystery", as a properly set up system will generate a core dump / WER dump pointing you exactly at the location where the crash occurred. /EHa will catch errors until your program state is so messed up that when you finally crash, you can't make nothing out of the core dump.Shaneka

© 2022 - 2024 — McMap. All rights reserved.