Is this a new syntax

P

parag_paul

Hi all
I am seeing the following code in one place

standalone line in some function ,

{
(void*) new (h) Class_Name(xip, virobj, type, true);
}

Does it make h an object of the Class_Name class
-Parag
 
M

Marcel Müller

Alf said:
Sorry, that last assumes that it's written by an ordinary programmer.

It /could/ be that it's written by an expert.

But if not, then it's probably chock full of bugs.

I wonder a bit about the differentiation between ordinary and expert
programmer. Most probably there are not that many people around, that
use this syntax without the adequate knowledge. In fact I have never
seen any buggy implementation with this syntax.
At some locations it is really useful. E.g. STL containers can reserve
space for new elements without calling the standard constructor
immediately. As far as I remember std::vector works this way to deal
with the logical size versus the allocation size.
Or I have implemented a simple, highly efficient, Java like, immutable
string class, that takes only the memory footprint of char* per instance
and the string value plus two ints header per different string value
(reference counted).

So, the syntax is only rarely needed. But if so, it is very important.
It is something like a back door for special purposes to call the
constructor directly. Calling a destructor explicitly is not less
dangerous and looks quite normal.


Marcel
 
K

kwikius

Marcel Müller said:
I wonder a bit about the differentiation between ordinary and expert
programmer. Most probably there are not that many people around, that use
this syntax without the adequate knowledge. In fact I have never seen any
buggy implementation with this syntax.

I agree. placement new is a component of non placement new too. its nothing
mysterious.
In fact non placement new is two distinct operations consisting of
allocating memory and calling placement new.
explaining "new" by decomposition of new into these two very different
components helps to explain its workings.
Its particularly nice as it reveals the transition from raw hardware into
the higher level realms of "type".

Also useful for smart pointers ...;-)

regards
Andy Little
 
K

kwikius

Alf P. Steinbach said:
* Marcel Müller:

Experts know that they don't know all. The old definition of an expert is
one who has made almost every conveivable error in the field of expertise,
and learned from that. Still, also experts make mistakes, even /trivial/
newbie mistakes; the main difference is that they know it and that they
deal with it, and that they tend to more often choose paths that turn out
to not be dead-ends.



Any buggy implementation of what?

Most uses of placement new are ill-conceived attempts at premature
optimization.

I have mostly only seen such uses of placement new, but that may be
because I don't usually delve into the innards of code that deals with
memory allocation proper (like std::vector code).



STL containers are implemented by experts and very very thoroughly tested,
so that ordinary programmers don't have to deal with the things they do
inside but can rely on the safe functionality that's exposed.



I think that's an example of non-expert use, evil premature
micro-optimization using the most dangerous low-level tool available. At
least, it sounds like you're allocating 1 chunk storage for the string
data plus two ints, and rely on implicit knowledge (not dynamically
represented) in the carrier. If so, then in addition to possible
alignment bugs, that means that you /inefficiently/ have to dynamically
allocate some variable amount of storage for every different string, when
the cost of allocation is precisely what you try to avoid. ;-)



No, placement new doesn't call a constructor "directly". The constructor
arguments you pass are forwarded to a constructor, not passed directly as
in an ordinary function call. In between there's a lot going on,
including preparation of cleanup in the event that the constructor throws.

Not sure what you mean.

Below is some code and some asm interspersed at the significant line 42

Therein placement new doesnt do much just, returns the address to construct
at AFAICS.
And N.B its more obvious doing it this way than munging two types of errors
in standard new.
IOW this should help to see what exceptions can be thrown from nonplacemnt
new more clearly
thus unravelling yet another bob.

regards
Andy Little

BTW I dont think the standard explains this very well ! It makes it sound
awfully complicated....

#include <iostream>
#include <stdexcept>
#include <string>
#include <cstring>

struct my{

my()
: text(0)
{
std::cout << "Enter some text\n";
std::string str;
getline(std::cin,str);
if (str == "throw"){
throw std::logic_error("DUMMY! I TOLD YOU not to input \"throw\"");
}
text = new char [str.length()+1];
strcpy(text,str.c_str());
}
~my()
{
std::cout << "text = \"" << text << "\" in my dtor \n";
delete [] text;
}
private:
my (my const &);
my & operator = ( my const &);

char * text;

};

int main()
{
// (malloc guarantees alignment)
void * allocation = malloc(sizeof (my));
if(allocation != NULL){
//construct a my at allocation
// input at prompt "throw" to throw
my* pmy=0;
try{
pmy = new (allocation) my(); // Line 42
pmy->~my();
//ASM
; Line 42
mov esi, edi
mov DWORD PTR __$EHRec$[ebp+12], 1
call ??0my@@QAE@XZ ; my::my
; Line 43
mov esi, eax
mov BYTE PTR __$EHRec$[ebp+12], bl
mov DWORD PTR _pmy$17011[ebp], eax
call ??1my@@QAE@XZ ; my::~my
/// end ASM
pmy=0;
}
catch(std::exception & e){
std::cout << e.what();
if(pmy){
std::cout << " and throw... twas messy bad \n";
}else{
std::cout << ", ... but throw.. twas neat \n";
}
}
free(allocation);
}
}
 
K

kwikius

Alf P. Steinbach said:
* kwikius:

In general it has to arrange for calling the corresponding deallocation
function when a constructor throws. For a custom allocation function this
is the only way that the corresponding custom deallocation function can be
implicitly called, and it implies that for this, the allocation function
argument(s) must be stored, and an exception handler set up.

Well I dont see the O.P. expression as *allocation*. IMO its construction at
a preallocated address.
For placement new of array of objects, if the exception will eventually be
caught higher up somewhere, then it also has to arrange for calling
destructors of already successfully constructed objects, in reverse order.
However, I fail to find this requirement in the standard. It must be
there somewhere...

I modified my original code, shows that (this is I think internal
implementation level stuff now) the implementation pushes the address of the
dtor, and does unwind back sequence on exception

Now with placement new array ("construct n elements at base memory
address")it is less trivial to use malloc. There is some housekeeping data
required presumably at the head of the allocation. I assumed here its size
of a pointer.

Nevertheless I don't agree that its that difficult or experts only. One
thing that C++ has and more modern languages which are biting at C++ heels
don't , is direct access to the hardware. Its a pain in the ass from some
viewpoints (e.g the "portable" size of int etc), but it might as well be
pointed out as a very powerful and instructive feature. It shows you the
relation between software and hardware.

Of course that assumes that hardware stays "rigid", though one day it may
become more "miscible" with software :



:)


Anyway here is the code:

#include <iostream>
#include <stdexcept>
#include <string>
#include <cstring>
#include <malloc.h>
/*sic*/
#include <new>

struct my{

my()
: text(0)
{
std::cout << "Enter some text\n";
std::string str;
getline(std::cin,str);
if (str == "throw"){
throw std::logic_error(" I TOLD YOU not to input \"throw\" ...
DUMBASS");
}
text = new char [str.length()+1];
strcpy(text,str.c_str());
}
~my()
{
std::cout << "text = \"" << text << "\" in my dtor \n";
delete [] text;
}
private:
my (my const &);
my & operator = ( my const &);

char * text;
};

template < typename T>
void deconstruct(T* p)
{
p->~my();
}
template <typename T>
void deconstruct(T* p, size_t n)
{
for (size_t i = 0; i != n; ++i){
deconstruct(&p);
}
}

void* array_malloc(size_t size)
{
//allow size_t for house_keeping
//implementation defined
#ifdef _MSC_VER
// don't quote me ;-)
static const int ar_info = sizeof(size_t*);
#endif
return malloc(size + ar_info);
}

int main()
{
static const size_t NUM =2;

// (malloc guarantees alignment)
void * allocation = array_malloc(sizeof (my) * (NUM));
my* pmy=0;
if(allocation != NULL){
//construct a my at allocation
// input at prompt "throw" to throw
try{
/*sic */
pmy = ::new (allocation) my[NUM]; // Line 71
deconstruct(pmy,NUM);
/*
ASM-----------
; Line 71
push OFFSET ??1my@@QAE@XZ ; my::~my
push OFFSET ??0my@@QAE@XZ ; my::my
push 2
push 4
lea esi, DWORD PTR [ebx+4]
mov DWORD PTR __$EHRec$[ebp+12], 1
push esi
mov DWORD PTR [ebx], 2
call ??_L@YGXPAXIHP6EX0@Z1@Z
mov edi, esi
mov BYTE PTR __$EHRec$[ebp+12], 0
mov DWORD PTR _pmy$[ebp], edi
; Line 72
xor esi, esi
npad 7
$LL20@main:
cmp esi, 2
je SHORT $LN13@main
lea ecx, DWORD PTR [edi+esi*4]
call ??1my@@QAE@XZ ; my::~my
inc esi
jmp SHORT $LL20@main
//end ASM ---------------
*/

}
catch(std::exception & e){
std::cout << e.what();
if(pmy){
std::cout << " and throw... twas messy bad \n";
}else{
std::cout << ", ... but throw.. twas neat \n";
}
}
free(allocation);
}
}

By the way, there's no placement delete: no way to pass deallocation
function arguments in a delete expression.

The standard tells you what placement new syntax is required to achieve.

The generated assembly code tells you how a given compiler implements
those requirements for any given special case.

For example, if the deallocation function does nothing, then it would be
silly to not optimize that part away. :)

That is the default for placement new at an address. I dont see it as
allocation. I see it as constructing an object at an address. I think that
is what the O.P. code was too..

And in this case it can be seen as a direct call to ctor IMO.
Cheers, & hth.,

- Alf

PS: In order to use placement new for in-place construction (which is
where the name comes from), formally you need to include <new>, and
practically it's a good idea to then use "::" qualification (§5.3.4/5) in
order not to pick up the class' own user defined allocation function, if
it has one.

OK.
 
P

Puppet_Sock

Experts know that they don't know all.  The old definition of an expert is one
who has made almost every conveivable error in the field of expertise, and
learned from that.

Heh heh. So an expert is somebody who no longer adds strtok
to codes, but knows how to debug it when it's already there.
Socks
 
J

James Kanze

Well I dont see the O.P. expression as *allocation*. IMO its
construction at a preallocated address.

The standard sees it as allocation; an operator new function is
an "allocator", and a new expression will always call an
allocator function. (IIRC, too, there was no indication in the
original posting as to the type of the argument to new, so we
don't know whether it was the default placement new or not.)

Of course, at a higher level, it is constructing an object at a
predetermined address.
Nevertheless I don't agree that its that difficult or experts
only.

I don't think that it's that difficult, but the need should
rarely present itself unless you're working on an implementation
of the basic library---in which case, you probably should be an
expert. In the past, of course, before the standard library
became widespread, most programmers had used it once or twice,
since most programmers had their own versions of something like
std::vector.
 
K

kwikius

The standard sees it as allocation; an operator new function is
an "allocator", and a new expression will always call an
allocator function. (IIRC, too, there was no indication in the
original posting as to the type of the argument to new, so we
don't know whether it was the default placement new or not.)

The arg name is "h". I guess h --> HANDLE ==void * (IIRC) from MS Windows
GlobalAlloc or some such.

regards
Andy Little
 
J

James Kanze

* kwikius:
Hm, I see James has already answered this. But I think it
would be less than polite if I let that answer serve as my
own, with no genuine Alf-input on the matter. So.
Regarding the first sentence, terminology. In the standard's
sense it's an allocation. Although as usual I can't find that
directly in the standard (the standard is notoriously
deficient in defining its terminology, or even using a
consistent terminology!), as with other implicitly defined
terms you can't make sense of things without assuming this
definition. For example, §5.3.4/5 has the condition "When the
allocated object is an array". And §5.3.4/5 applies to
non-placement new-expressions as well as placement
new-expressions, and since something was "allocated", there
had to be something called an "allocation".

§3.7.3.1 gives all the details about allocator functions...
except the fact that they are spelled "operator new", and that
all "operator new" are allocator functions:). The firsrt
paragraph of §3.7.3 does mention that operator new is the global
allocator function. But it's still not really explicit:
"allocator function" == "function named operator new", but as
you say, while the standard doesn't really make sense otherwise,
it doesn't come out and say so in so many words.
Regarding the second sentence, construction at a pre-allocated
address, I have to take exception to James' statement that "Of
course, at a higher level, it is constructing an object at a
predetermined address.". Perhaps James didn't mean "higher
level" but "at the end" or something like that.

Sort of. "A higher level" is really sloppy wording, because it
doesn't mean anything: a higher level of what? What I meant was
a higher level of abstraction. What you consider it doing at
the design level.
A placement new expression does in general not construct at a
predetermined address, because in general the allocation
function will really allocate, not just take a pointer
argument and return it as void*.
And in the OP's case
(void*) new (h) Class_Name(xip, virobj, type, true);
we don't know the type of the allocation function argument
(the 'h'), and we don't know the definition of 'Class_Name',
more precisely, whether it has a custom allocation function,
and so in two different ways we don't know what the effect of
the placement new expression is.

In fact, all we know is that the programmer probably wasn't too
sure of what he was doing, since he felt obliged to cast the
results of the new expression to void*. I can't think of any
case where that would be appropriate (and if it were, I'd
definitely use static_cast, because it's rare enough that I'd
want to call attention to the fact).

Which sort of confirms your conclusion that the code wasn't
written by an expert.
 
K

kwikius

In fact, all we know is that the programmer probably wasn't too
sure of what he was doing, since he felt obliged to cast the
results of the new expression to void*. I can't think of any
case where that would be appropriate (and if it were, I'd
definitely use static_cast, because it's rare enough that I'd
want to call attention to the fact).

Which sort of confirms your conclusion that the code wasn't
written by an expert.


if you do

#define HANDLE void*

HANDLE h1 = (HANDLE) new (h) Class_Name(xip, virobj, type, true);
/*...*/
SetProp( hMyWindow,"MyProp",h1); // Set a generic window property

then the code looks pretty much like a lot of other stuff in the windows
SDK source. C-style casts are the only casts used.

Its up to you whether you wish to call the Windows SDK authors experts or
not (I would say yes), but the SDK is written in C and that works very well
because as we all know the C++ ABI is not portable between compilers.

I read the use of the C-style cast as a statement along the lines of "OK
back to the C ABI" and I use it myself in that sense.

regards
Andy Little
 
K

kwikius

Alf P. Steinbach said:
* kwikius:


Hm, the SDK is written in C, and the C++ statement looks like that.

This is interesting... :)

Well I guess you are being sarcastic. Otherwise I don't know what you mean.
Anyway Open up <windows.h> and you will see all the API calls are C calls.
OTOH you are referring to not using ::SetProp? Sure I use global scope op
for C API calls. Its a nitpick for the example.

Windows defines a fairly portable, but limited, C++ ABI, namely COM.

Now it has been superceded by .NET AFAIK which does have a portable ABI
among languages anyawy.
Presumably in this context the cast was used to shut up an overzealous
compiler that insists on warning about just about everything, but an
alternative explanation is a coding guideline where the cast indicates
"this value is intentionally discarded", i.e. that not storing that
pointer is intentional.

I assumed that code was snipped omitting an assignment else cast seems
pretty pointless.
As I say I find C-style cast useful to document code for Windows API.

regards
Andy Little
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,173
Messages
2,570,939
Members
47,484
Latest member
JackRichard

Latest Threads

Top