• Create Account

Interested in a FREE copy of HTML5 game maker Construct 2?

We'll be giving away three Personal Edition licences in next Tuesday's GDNet Direct email newsletter!

### #ActualHodgman

Posted 03 January 2013 - 07:41 PM

I read somewhere that you can't add pointers in C++, only subtract them, but to offset local pointers you needed to add!

Adding two pointers doesn't really make sense, but you're not trying to add two pointers! :-)

You've got a pointer to your memory block, and you've got an offset (which is an integer) that you want to add together. Adding pointers and integers is well defined -- adding 42 to a pointer of type "T*" will give you a pointer that's advanced by sizeof(T)*42 bytes, and seeing that sizeof(char)==1 adding integers to char*-type pointers is a fairly intuitive and common way of performing "pointer math".

Usually you'd do something like this to perform "pointer patching" -- converting local file offsets to real pointers:

struct Header
{
union
{
Foo* foo_pointer;
int foo_offset;
}
};
static_assert( sizeof(int) == sizeof(void*) );//the int type in the union should be the same size as a pointer type.
//n.b. this means your file generator (C# tool) has to be aware of whether it's generating files for a 64-bit or 32-bit application!

char* memblock = new char[]...
header->foo_pointer = (Foo*)(memblock + header->foo_offset); //n.b. memblock is a char*

Your code is OK though, because casting a pointer to a ptrdiff_t and back to a pointer works on every compiler I've ever used ;)

However, you can replace "memOffset" with "memblock" and it will work the same, but look a bit more intuitive.

Also, your casting of your "file offset pointers" to integers via e.g. "(ptrdiff_t)model->MeshHeaders" is basically equivalent to my union above, so there's no need to change it if it's more intuitive for you to do it this way.

In my engine, to make my file-formats independent of the actual pointer size (32/64 bit), and to simplify my loading routines, I usually avoid performing pointer-patching on-load, and instead do it on-demand each time the "pointer" is used. Also, I use offsets that are relative to the position of the offset variable itself, rather that ones that are relative to the beginning of the file to facilitate this. If you're interested, see the Offset class in this header (also, Address is used for offsets that are relative to the beginning of some memory-block).

The next problem arrived when I realized that I needed to somehow delete the model from memory as well. Again not sure, as I had casted a char[] to a model, if I could delete the model. I pretended I could and wrote the destructor. Miraculously it seemed to work!

It might seem to work, but that sounds very bad. If the memory was created with char* buffer = new char [size], then it needs to be deleted with delete [] buffer (where buffer is a char*).

This gets complicated because in your file-failure case, you are allocating the memory with model = new Ruined::Graphics::Model(), in which case it needs to be deleted with delete model (where model is a Model*).

Personally, I'd remove that failure case and return NULL, or change it to allocate the memory consistently, with:

char* buffer = new char [sizeof(Ruined::Graphics::Model)]

Further, you can't just use a regular shared_ptr to clean up after you, because it will use the wrong type of delete -- you need to configure it to use a custom deleter that calls your own "destructor" function and then deletes the char array properly.

### #6Hodgman

Posted 03 January 2013 - 07:39 PM

I read somewhere that you can't add pointers in C++, only subtract them, but to offset local pointers you needed to add!

Adding two pointers doesn't really make sense, but you're not trying to add two pointers! :-)

You've got a pointer to your memory block, and you've got an offset (which is an integer) that you want to add together. Adding pointers and integers is well defined -- adding 42 to a pointer of type "T*" will give you a pointer that's advanced by sizeof(T)*42 bytes, and seeing that sizeof(char)==1 adding integers to char*-type pointers is a fairly intuitive and common way of performing "pointer math".

Usually you'd do something like this to perform "pointer patching" -- converting local file offsets to real pointers:

struct Header
{
union
{
Foo* foo_pointer;
int foo_offset;
}
};
static_assert( sizeof(int) == sizeof(void*) );//the int type in the union should be the same size as a pointer type.
//n.b. this means your file generator (C# tool) has to be aware of whether it's generating files for a 64-bit or 32-bit application!

char* memblock = new char[]...
header->foo_pointer = (Foo*)(memblock + header->foo_offset); //n.b. memblock is a char*

Your code is OK though, because casting a pointer to a ptrdiff_t and back to a pointer works on every compiler I've ever used ;)

However, you can replace "memOffset" with "memblock" and it will work the same, but look a bit more intuitive.

Also, your casting of your "file offset pointers" to integers via e.g. "(ptrdiff_t)model->MeshHeaders" is basically equivalent to my union above, so there's no need to change it if it's more intuitive for you to do it this way.

In my engine, to make my file-formats independent of the actual pointer size (32/64 bit), and to simplify my loading routines, I usually avoid performing pointer-patching on-load, and instead do it on-demand each time the "pointer" is used. Also, I use offsets that are relative to the position of the offset variable itself, rather that ones that are relative to the beginning of the file to facilitate this. If you're interested, see the Offset class in this header.

The next problem arrived when I realized that I needed to somehow delete the model from memory as well. Again not sure, as I had casted a char[] to a model, if I could delete the model. I pretended I could and wrote the destructor. Miraculously it seemed to work!

It might seem to work, but that sounds very bad. If the memory was created with char* buffer = new char [size], then it needs to be deleted with delete [] buffer (where buffer is a char*).

This gets complicated because in your file-failure case, you are allocating the memory with model = new Ruined::Graphics::Model(), in which case it needs to be deleted with delete model (where model is a Model*).

Personally, I'd remove that failure case and return NULL, or change it to allocate the memory consistently, with:

char* buffer = new char [sizeof(Ruined::Graphics::Model)]

Further, you can't just use a regular shared_ptr to clean up after you, because it will use the wrong type of delete -- you need to configure it to use a custom deleter that calls your own "destructor" function and then deletes the char array properly.

### #5Hodgman

Posted 03 January 2013 - 07:37 PM

I read somewhere that you can't add pointers in C++, only subtract them, but to offset local pointers you needed to add!

Adding two pointers doesn't really make sense, but you're not trying to add two pointers! :-)

You've got a pointer to your memory block, and you've got an offset (which is an integer) that you want to add together. Adding pointers and integers is well defined -- adding 42 to a pointer of type "T*" will give you a pointer that's advanced by sizeof(T)*42 bytes, and seeing that sizeof(char)==1 adding integers to char*-type pointers is a fairly intuitive and common way of performing "pointer math".

Usually you'd do something like this to perform "pointer patching" -- converting local file offsets to real pointers:

struct Header
{
union
{
Foo* foo_pointer;
int foo_offset;
}
};
static_assert( sizeof(int) == sizeof(void*) );//the int type in the union should be the same size as a pointer type.
//n.b. this means your file generator (C# tool) has to be aware of whether it's generating files for a 64-bit or 32-bit application!

char* memblock = new char[]...
header->foo_pointer = (Foo*)(memblock + header->foo_offset); //n.b. memblock is a char*

Your code is OK though, because casting a pointer to a ptrdiff_t and back to a pointer works on every compiler I've ever used ;)

However, you can replace "memOffset" with "memblock" and it will work the same, but look a bit more intuitive.

Also, your casting of your "file offset pointers" to integers via e.g. "(ptrdiff_t)model->MeshHeaders" is basically equivalent to my union above, so there's no need to change it if it's more intuitive for you to do it this way.

In my engine, I usually avoid performing pointer-patching on load, and instead do it on-demand each time the "pointer" is used. Also, I use offsets that are relative to the position of the offset variable itself, rather that ones that are relative to the beginning of the file to facilitate this. See the Offset class in this header.

The next problem arrived when I realized that I needed to somehow delete the model from memory as well. Again not sure, as I had casted a char[] to a model, if I could delete the model. I pretended I could and wrote the destructor. Miraculously it seemed to work!

It might seem to work, but that sounds very bad. If the memory was created with char* buffer = new char [size], then it needs to be deleted with delete [] buffer (where buffer is a char*).

This gets complicated because in your file-failure case, you are allocating the memory with model = new Ruined::Graphics::Model(), in which case it needs to be deleted with delete model (where model is a Model*).

Personally, I'd remove that failure case and return NULL, or change it to allocate the memory consistently, with:

char* buffer = new char [sizeof(Ruined::Graphics::Model)]

Further, you can't just use a regular shared_ptr to clean up after you, because it will use the wrong type of delete -- you need to configure it to use a custom deleter that calls your own "destructor" function and then deletes the char array properly.

### #4Hodgman

Posted 03 January 2013 - 07:35 PM

I read somewhere that you can't add pointers in C++, only subtract them, but to offset local pointers you needed to add!

Adding two pointers doesn't really make sense, but you're not trying to add two pointers! :-)

You've got a pointer to your memory block, and you've got an offset (which is an integer) that you want to add together. Adding pointers and integers is well defined -- adding 42 to a pointer of type "T*" will increment the pointer by sizeof(T)*42 bytes, and sizeof(char)==1, so adding integers to char*-type pointers is a fairly intuitive and common way of performing "pointer math".

Usually you'd do something like this to perform "pointer patching" -- converting local file offsets to real pointers:

struct Header
{
union
{
Foo* foo_pointer;
int foo_offset;
}
};
static_assert( sizeof(int) == sizeof(void*) );//the int type in the union should be the same size as a pointer type.
//n.b. this means your file generator (C# tool) has to be aware of whether it's generating files for a 64-bit or 32-bit application!

char* memblock = new char[]...
header->foo_pointer = memblock + header->foo_offset;

Your code is OK though, because casting a pointer to a ptrdiff_t and back to a pointer works on every compiler I've ever used ;)

However, you can replace "memOffset" with "memblock" and it will work the same, but look a bit more intuitive.

Also, your casting of your "file offset pointers" to integers via e.g. "(ptrdiff_t)model->MeshHeaders" is basically equivalent to my union above, so there's no need to change it if it's more intuitive for you to do it this way.

In my engine, I usually avoid performing pointer-patching on load, and instead do it on-demand each time the "pointer" is used. Also, I use offsets that are relative to the position of the offset variable itself, rather that ones that are relative to the beginning of the file to facilitate this. See the Offset class in this header.

The next problem arrived when I realized that I needed to somehow delete the model from memory as well. Again not sure, as I had casted a char[] to a model, if I could delete the model. I pretended I could and wrote the destructor. Miraculously it seemed to work!

It might seem to work, but that sounds very bad. If the memory was created with char* buffer = new char [size], then it needs to be deleted with delete [] buffer (where buffer is a char*).

This gets complicated because in your file-failure case, you are allocating the memory with model = new Ruined::Graphics::Model(), in which case it needs to be deleted with delete model (where model is a Model*).

Personally, I'd remove that failure case and return NULL, or change it to allocate the memory consistently, with:

char* buffer = new char [sizeof(Ruined::Graphics::Model)]

Further, you can't just use a regular shared_ptr to clean up after you, because it will use the wrong type of delete -- you need to configure it to use a custom deleter that calls your own "destructor" function and then deletes the char array properly.

### #3Hodgman

Posted 03 January 2013 - 07:21 PM

I read somewhere that you can't add pointers in C++, only subtract them, but to offset local pointers you needed to add!

Adding two pointers doesn't really make sense, but you're not trying to add two pointers. You've got a pointer to your memory block, and you've got an offset (which is an integer) that you want to add.
Adding pointers and integers is well defined. Usually you'd do something like this to perform "pointer patching" -- converting local file offsets to real pointers:

struct Header
{
union
{
Foo* foo_pointer;
int foo_offset;
}
};
static_assert( sizeof(int) == sizeof(void*) );//the int type in the union should be the same size as a pointer type.
//n.b. this means your file generator (C# tool) has to be aware of whether it's generating files for a 64-bit or 32-bit application!

char* memblock = new char[]...
header->foo_pointer = memblock + header->foo_offset;

In my engine, I usually avoid performing pointer-patching on load, and instead do it on-demand each time the "pointer" is used. Also, I use offsets that are relative to the position of the offset variable itself, rather that ones that are relative to the beginning of the file to facilitate this. See the Offset class in this header.

The next problem arrived when I realized that I needed to somehow delete the model from memory as well. Again not sure, as I had casted a char[] to a model, if I could delete the model. I pretended I could and wrote the destructor. Miraculously it seemed to work!

It might seem to work, but that sounds very bad. If the memory was created with char* buffer = new char [size], then it needs to be deleted with delete [] buffer (where buffer is a char*).

This gets complicated because in your file-failure case, you are allocating the memory with model = new Ruined::Graphics::Model(), in which case it needs to be deleted with delete model (where model is a Model*).

Personally, I'd remove that failure case and return NULL, or change it to allocate the memory consistently, with:

char* buffer = new char [sizeof(Ruined::Graphics::Model)]

Further, you can't just use a regular shared_ptr to clean up after you, because it will use the wrong type of delete -- you need to configure it to use a custom deleter that calls your own "destructor" function and then deletes the char array properly.

### #2Hodgman

Posted 03 January 2013 - 07:15 PM

I read somewhere that you can't add pointers in C++, only subtract them, but to offset local pointers you needed to add!

Adding two pointers doesn't really make sense, but you're not trying to add two pointers. You've got a pointer to your memory block, and you've got an offset (which is an integer) that you want to add.
Adding pointers and integers is well defined. Usually you'd do something like this to perform "pointer patching" -- converting local file offsets to real pointers:

struct Header
{
union
{
Foo* foo_pointer;
int foo_offset;
}
};
static_assert( sizeof(int) == sizeof(void*) );//the int type in the union should be the same size as a pointer type.
//n.b. this means your file generator (C# tool) has to be aware of whether it's generating files for a 64-bit or 32-bit application!

char* memblock = new char[]...
header->foo_pointer = memblock + header->foo_offset;

In my engine, I usually avoid performing pointer-patching on load, and instead do it on-demand each time the "pointer" is used -- see the Offset class in this header.

The next problem arrived when I realized that I needed to somehow delete the model from memory as well. Again not sure, as I had casted a char[] to a model, if I could delete the model. I pretended I could and wrote the destructor. Miraculously it seemed to work!

It might seem to work, but that sounds very bad. If the memory was created with char* buffer = new char [size], then it needs to be deleted with delete [] buffer (where buffer is a char*).

This gets complicated because in your file-failure case, you are allocating the memory with model = new Ruined::Graphics::Model(), in which case it needs to be deleted with delete model (where model is a Model*).

Personally, I'd remove that failure case and return NULL, or change it to allocate the memory consistently, with:

char* buffer = new char [sizeof(Ruined::Graphics::Model)]

Further, you can't just use a regular shared_ptr to clean up after you, because it will use the wrong type of delete -- you need to configure it to use a custom deleter that calls your own "destructor" function and then deletes the char array properly.

PARTNERS