Sign in to follow this  
adder_noir

Placement New and some things I've never seen before!!

Recommended Posts

Hi!,

Here's my latest stumper! Check out the code below:

class SerialPort
{

};

void* operator new(unsigned int, void* p) throw() // (size_t, void* p) was rejected by the compiler
{
return p;
}

int main()
{
const int comLoc = 0x00400000; // not seen this before
void* comAddr = reinterpret_cast<void*>(comLoc); // no idea why reinterpret cast is being used here
SerialPort* com1 = new(comAddr) SerialPort; // seems to act as if 2 arguments are being passed

return 1;
}




Ok I don't really know where to start with this one! I'll try tho. First things first.

1)The original argument list for the operator overload as written by my book's author went:
void* operator new(size_t, void* p) throw()
which was rejected by the compiler saying it had not been declared in this scope. Weird as the word size_t turned blue indicating the IDE recognised it as a keyword. I've asked this before but it was early on in my learning and I didn't really cement the understanding. Any ideas? Unsigned int ran fine.

2)The line with the address was weird. How can an int relate to an address
const int comLoc = 0x00400000;

3)I have no idea why reinterpret_cast is being used to initiate the variable 'comAddr'.

4)The line
SerialPort* com1 = new(comAddr) SerialPort;
acts as if two arguments are being passed to it yet we are only stating one. When I tried to add an int to it - new(1, comAddr) - the compiler complained no matching fucntion call and stated I was in essence passing three arguments! Any idea why this mysterious first argument is considered to be implied and not even required to be stated? Something native to operator new perhaps?

I have more questions on this chapter but this is enough for me to handle for now ;o) Thanks so much for any help anyone can offer.

Share this post


Link to post
Share on other sites
Quote:
Original post by adder_noir
which was rejected by the compiler saying it had not been declared in this scope. Weird as the word size_t turned blue indicating the IDE recognised it as a keyword.
C and C++ IDEs are not perfect. size_t is common type, but it still needs to be included via proper headers (depends on compiler and standard library). IDE, at best, just gives hints. Sometimes it's also flat out wrong.

Quote:
Unsigned int ran fine.
Though it's not the same. But this area is a mess, lots of platform specifics.

Quote:
2)The line with the address was weird. How can an int relate to an address
const int comLoc = 0x00400000;
Because that's what it is. Memory is linear. An address is offset in memory. You can think of it this way:
const int INSTALLED_RAM_SIZE = 2*1024*1024*1024;
char RAM[INSTALLED_RAM_SIZE];
Again, lots of very obscure, platform and architecture specific behavior, depending on memory models and much more.

But pointers in memory, no matter how abstract, are just a number, indicating the linear offset.

Quote:
3)I have no idea why reinterpret_cast is being used to initiate the variable 'comAddr'.
Because compiler tries to preserve sanity and hundreds of different types of bugs that can occur when numbers are treated as addresses. Even though they can be considered the same, there are many edge cases where they are not - and compiler isn't allowed to permit one be assigned to the other.

reinterpret_cast throws all safety away and assumes user is right - even if they are not.

Quote:
Any idea why this mysterious first argument is considered to be implied and not even required to be stated? Something native to operator new perhaps?
Yes. It's the number of bytes to allocate. This is the size_t/unsigned int in the function definition.


With this out of the way - forget everything above. It's wrong on way too many levels and makes waaaay too many assumptions which are unlikely to hold true. So even at best, such code working is closer to a miracle than clever tricks.

Share this post


Link to post
Share on other sites
An excellent and detailed reply thanks!

I noticed you mention some of this is platform specific. I get the impression size_t is something which is platform specific with regards the number of bytes as you say. So does this mean that say on a PC with a certain operating system or hardware configuration size_t might be say 4 bytes whereas on say a console it might be 2 bytes for example?

That would seem to support the concept of it being a fixed size for the given platform and thus not alterable therefore something which 'new' which handles memory would have passed to it implicitly not the way you can change regular int arguments to functions.

I see also that the memory address is really just a hex number and so is therefore an integer so to speak. Out of curiousity where would size_t normally be defined and what would the declaration look like? Why have I never needed to define it before in previous new operations is it just assumed to be say 4 bytes or something unless declared otherwise when you call 'new'? I wonder maybe does the variable type you use with new dictate this most of the time? A bit clueless to be honest!

I've got more questions for later this is enough for now cheers ;o)

Share this post


Link to post
Share on other sites
Quote:
Original post by adder_noir
I noticed you mention some of this is platform specific. I get the impression size_t is something which is platform specific with regards the number of bytes as you say. So does this mean that say on a PC with a certain operating system or hardware configuration size_t might be say 4 bytes whereas on say a console it might be 2 bytes for example?

Exactly, but you are more likely to run into 32 bits unsigned on some platforms and 64 bits unsigned on others in this modern age. When I wore a younger man's clothes integers as long as 16 bits were pretty exotic.
Quote:
Out of curiousity where would size_t normally be defined and what would the declaration look like? Why have I never needed to define it before in previous new operations is it just assumed to be say 4 bytes or something unless declared otherwise when you call 'new'? I wonder maybe does the variable type you use with new dictate this most of the time? A bit clueless to be honest!

Actually, there is no size_t in C++. The type ::std::size_t is defined in <cstddef>, but it is also required by other standard headers so <cstddef> could be pulled in implicitly by other headers (for example <new>, which you need to pull in if you're going to redefine ::operator new(std::size_t, void*)). You can not rely on this, though.

You may also find that your library implementation incorrectly injects some names from the ::std namespace into the :: namespace, most probably because it's actually hoisting a name from a C header into ::std. This is in fact non-conforming behaviour and you should not rely on it. Not all compilers do that, and yours might not in a future release.

Share this post


Link to post
Share on other sites
Quote:
Original post by Bregma
When I wore a younger man's clothes integers as long as 16 bits were pretty exotic.


The PDP-10 must have seemed like some kind of magical alien technology, then ;)

Share this post


Link to post
Share on other sites
Some good replies chaps thanks! I understand this well enough to progress through the rest of this chapter and start addressing the concept questions posed by the author now that I understand most of the syntax ;o)

Share this post


Link to post
Share on other sites
Ok chaps I have some questions relating to the topic of the chapter I'm reading.

The author of my book states there is a difference between the 'new' operator and 'operator new'. Apparently the 'new' operator can't be overloaded and always behaves the same way. It calls the function 'operator new' and initialises the returned storage. Right ok.

However he also goes on to write an overloaded version of 'operator new' which he says doesn't allocate any storage it just returns a pointer to some storage that is 'presumably' (his words) already initialised.

I have the following questions:

1)If the 'new' operator always calls the function 'operator new' then how is it possible to customise the 'operator new' function so that it deviates from the standard behaviour. He's just stated that the behaviour of the 'new' operator can't be changed, it initialises returned storage - yet within the same paragraph he states that he's overloaded 'operator new' to not initialise the returned storage. This would be ok of they were unrelated, but they're not. 'New' operator according to the book calls 'operator new'. So one moment he states the behaviour is non-changeable, and the next he states he's successfully changed it? Seems rather contradictory to me.

2)A reminder of the source code:
class SerialPort
{

};

void* operator new(size_t, void* p) throw()
{
return p;
}

int main()
{
const int comLoc = 0x00400000;
void* comAddr = reinterpret_cast<void*>(comLoc);
SerialPort* com1 = new(comAddr) SerialPort;

return 1;
}

Apparently no storage is allocated by 'operator new', yet we have created an object (SerialPort). Ok well if there is no storage allocated then where the hell does the created object go?

I really don't like this chapter, it seems to be messing with things that look dead certain to create nasty compile-time and run-time errors to me. That's just my impression though, I am new aterall ;o)

Any ideas? Thanks for any help anyone can give ;o)

Share this post


Link to post
Share on other sites
You should just drop whatever book you have and find something else..rofl. That is, unless you REALLY have an urge to understand what the author is doing. In that case, you might want to learn assembly language. ;o

Share this post


Link to post
Share on other sites
Quote:
Original post by adder_noir
Any ideas? Thanks for any help anyone can give ;o)

The standard library provides six overloads of ::operator new() in the header <new>. There is the one used by the new operator, the one use by the new[] operator, the nothrow variants of the above, and the ones used by placement new and placement new[]. It also contains the matching overloads of ::operator delete.

You can replace these overloads and affect the various new operators, or you can provide additional overloads and invoke them using extended new operator syntax. These overloads provide a customization point of the language's allocation behaviour through the standard library. You can also provide class-specific operator new and operator delete functions.

The classic reference works on the C++ standard library detail how these work and how to give additional overloads. Consult Stroustrup's "The C++ programming Language" and Josuttis' "The C++ Statndard Library, A Tutorial And Reference" as well as Meyers' "Effective C++" series and Sutter's "Exceptional C++" for in-depth examination of usage and gotchas.

Share this post


Link to post
Share on other sites
Quote:
Original post by adder_noir

2) Apparently no storage is allocated by 'operator new', yet we have created an object (SerialPort). Ok well if there is no storage allocated then where the hell does the created object go?

I really don't like this chapter, it seems to be messing with things that look dead certain to create nasty compile-time and run-time errors to me. That's just my impression though, I am new aterall ;o)

Any ideas? Thanks for any help anyone can give ;o)


The object com1 will simply occupy the memory at 0x400000, whatever that may be.

If I were to guess, this book was written for systems without protected memory. And this portion wants to create an instance of a class that directly communicates over the serial port, which presumably resides at memory address 0x400000 on said system.

This is however not the case on modern operating systems, and doing anything with obj1 created this way will most likely cause run-time crashes.

Share this post


Link to post
Share on other sites
Quote:
Original post by Zahlman
Quote:
Original post by Bregma
When I wore a younger man's clothes integers as long as 16 bits were pretty exotic.

The PDP-10 must have seemed like some kind of magical alien technology, then ;)

It was a PDP-11/35 and yes, it was exotic compared with the 6502 and 1802 microprocessors I was familiar with. Not that I had a C++ compiler, but FORTRAN IV's INTEGER*4 was better than a MOV instruction into the accumulator. I will now stroke my neckbeard.

Share this post


Link to post
Share on other sites
Ok thanks for the replies chaps. I think maybe this isn't the best book in the world for me. I'm going to move on to the next chapter anyway I think.

I might pickup a copy of Stroustroup's book instead after reading through this one and understanding what I can. What do you think?

Cheers ;o)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this