Skip to main content
GameDev.net gamedev.net
🔒 Locked

Casting an interface onto data

Started by Kest Oct 11, 2006 at 3:27 AM 10 replies 1.1k views
Original Post
Kest
Kest
Can I do this safely?

class PtrThing
{
   PtrThing() { data = new int[**]; }
   ~PtrThing() { delete [] data; }

   ... big interface ...

   static const PtrThing& GetTempInterface(const int* &d) { return *((PtrThing*)&d); }

private:
   int *data;
};

...

int *custom_data = GetCustomData(..);
const PtrThing &ref20 = PtrThing::GetTempInterface( custom_data + 20 );



Since the class object only owns the data member, and belongs to no hierarchy, is it safe to assume the data member is the only memory it owns? Is it safe to assume PtrThing is synonymous with int* ? edited: I forgot my reference (&) operators. [Edited by - Kest on October 11, 2006 3:57:59 AM]
Skizz
Skizz
There is already a facility in C++ to do what you want - it's called "placement new" and works like this:
ClassA *ptr = new (memory) ClassA (/*constructor args*/);


Be careful about vtbls though. It won't work too well with classes that have vtbls in that the data in 'memory' will be overwritten with the vtbl pointer.

Skizz
Zahlman
Zahlman
Um, that looks a lot like you're trying to reinvent std::vector, except rather unsafely (i.e. without a size or capacity count). What's really going on here?
Kest
Kest
Quote:
Original post by Skizz
There is already a facility in C++ to do what you want - it's called "placement new" and works like this:
ClassA *ptr = new (memory) ClassA (/*constructor args*/);


Be careful about vtbls though. It won't work too well with classes that have vtbls in that the data in 'memory' will be overwritten with the vtbl pointer.

Skizz

The problem is that the class could easily overwrite the memory during construction, member function calls, and destruction. Actually, I'm not sure how that would be avoided. The initial (memory) is const.

Doing the cast to reference avoids calling constructors and destructors. It doesn't create any instances of the object. As long as the reference is const, and the class has no v-table, I don't think any functions being called with the interface could become a problem. Both of those aspects can be controlled from the class declaration.

I appreciate the help, though.

Quote:
Original post by Zahlman
Um, that looks a lot like you're trying to reinvent std::vector, except rather unsafely (i.e. without a size or capacity count). What's really going on here?

The class was fabricated for the post. I want to apply my string interface to memory in the middle of a file buffer.
jpetrie
jpetrie
Okay. Let me get this straight before I propose a solution.
You're stay you have a large buffer of memory, say
unsigned char *gBuffer = /* memory obtained somehow */;


At some particular, defined offset within that buffer
size_t gOffet = /* some offset (in bytes) within gBuffer */;

you "know" that there is an object of type T, and you want a reference to that object
T& gObject = *reinterpret_cast< T* >(gBuffer + gOffset);


and you want to do it this way, instead of with placement new, since you don't want constructors to be called because they would (presumably) initialize the memory block, and you want to preserve the values already in the memory block, which you "know" represent an already-initialized object of T.

Is that correct, or do you really need to have the strange issue of the class itself holding onto the buffer pointer and extracting "instances" of itself from that buffer?) The obvious initial issues are with "knowing" the memory actually points to what you want, how you obtained that memory in the first place (relating to ensuring that data aligns). What exactly do you need this for? Serialization? Is the class you are casting to a template type, or is it always known / always "itself."

To address specific issues:
Quote:

is it safe to assume the data member is the only memory it owns? Is it safe to assume PtrThing is synonymous with int* ?

No, and not at all.

Quote:

I don't think any functions being called with the interface could become a problem.

The interface might have mutable data members. If it isn't a POD type, it's layout is implementation defined, which presents serious potential issues if multiple-compiler support is an issue.

I'm working largely on assumptions here, so if you could provide more information about what you're trying to ultimately accomplish, and why, it would help.

[Edited by - jpetrie on October 11, 2006 9:20:57 AM]
Zahlman
Zahlman
Quote:
Original post by Kest
apply my string interface to memory in the middle of a file buffer.


Oooh. Those would be the magic words for comprehension, yes. And I take it you want to "deal with" chunks of unknown size, yes?

Well, first of all, the class (as illustrated) just contains a pointer, so one thing that springs to mind is to set an "ownership" flag, and construct the object from a raw pointer. You'll also want to make it non-copyable to avoid *serious* memory-management headaches (trust me - bad mojo trying to work with this complicated an ownership policy + copying).

class PtrThing {  bool owned;  int *data;  PtrThing(const PtrThing& other); // don't implement this anywhere  PtrThing& operator=(const PtrThing& rhs); // ditto  public:  PtrThing(int size) : owned(true), data(new int[size]) {}  ~PtrThing() { if (owned) delete[] data; }  PtrThing(int* toWrap) : owned(false), data(toWrap) {}  // etc.};


However, I think a much better idea would be to:

1) Rewrite the interface as free functions. Have them accept the pointer "to beginning of buffer" as an argument. Then just use that in the middle of the file buffer as needed.

2) If you find that the class would still be useful, re-implement the member functions by having them delegate to the new free functions (passing the internal pointer as the "beginning of buffer" argument).

And better still:

0) Post some of the code of this interface and let's see if the standard library can't solve your problems in a much more straightforward way ;)
Kest
Kest
Quote:
Original post by jpetrie
Is that correct, or do you really need to have the strange issue of the class itself holding onto the buffer pointer and extracting "instances" of itself from that buffer?) The obvious initial issues are with "knowing" the memory actually points to what you want, how you obtained that memory in the first place (relating to ensuring that data aligns).

My example code after the "..." is incorrect. I would have needed to declare a pointer variable to point to 'custom_data + 20' before I could cast it to the class type. I don't think the + 20 would have compiled. My apologies for so many errors.

Quote:
What exactly do you need this for? Serialization?

I just want to use the class interface on the buffer without allocating a copy of the buffer in the class constructor.

A win win situation would be to declare an instance of the class using a special constructor (which I could add now) which would cause the destructor to not be called when the object is removed from scope. But I don't think this is possible.

Is there any way to prevent an object's destructor from being called?

Thanks for the help
Kest
Kest
Quote:
Original post by jpetrie
Quote:

is it safe to assume the data member is the only memory it owns? Is it safe to assume PtrThing is synonymous with int* ?

No, and not at all.

I'm in some pretty deep dirt, then. Most of my file read/write routines work based on the fact that the size of simple structures is the size of the data contained in those structures. Are you suggesting this can change during runtime? Runtime is all I'm concerned about. Code portability isn't an issue. If I own a pointer to a structure which has one member variable, and own a pointer to that structure's member variable, is it not a definite that those two pointers will be equal? If not, then pointers to structures are not guaranteed to point to their data? Like I said, deep dirt.

Quote:
Original post by Zahlman
bool owned;

This is exactly the behavior I want, so you hit my goal right on. I would just prefer not to add extra data members to the class. Would there be any possibility of adding a specifier to the class instance on decleration to make it const and non-destructable (as in, no destructor called)?

Quote:
However, I think a much better idea would be to:

Global functions seem to be the only acceptable alternative to modifying the class.
jpetrie
jpetrie
Quote:

I'm in some pretty deep dirt, then. Most of my file read/write routines work based on the fact that the size of simple structures is the size of the data contained in those structures.


For simple (POD) structures, this is true, and the layout in memory is the same as the layout in the declaration of the POD structure, plus any padding the compiler may have added.

For non-POD structures, all bets may be off.

Quote:

Is there any way to prevent an object's destructor from being called?

Sure:
1) Don't create it, or
2) Don't delete it.

Option (2) involves leaking, for example, by creating an instance on the heap and never calling delete. This is pretty bad, obviously.

Option (1) involves (beside the obvious) using placement new.

unsigned char *buffer = new buffer[sizeof(T)];T*             t      = new(buffer) T();  // must call t->~T() to end the lifetime of t.  // this will not call the destructor automatically:  delete[] buffer;


This is bad, too, obviously, but it might be forgivable if you are wrapping this kind of thing up.

Thing is, if you want the "casted instance" to take on the data of the buffer (assuming you got alignment correct), how did you get that data there in the first place? Did you manually write it there? Did you read it from a file?

Either way is pretty brittle... You say you have a buffer of data you want to "cast" your string class on to (why are you not using std::string?). But if the buffer looks like, say, "foobarbazHello, World!asdf" and your string looks anything like what a string class should look like (containing, say, a char* to the data), then attempting to cast the string interface onto the data buffer won't do what you want -- it will just interpet the sizeof(char*) bytes that end up overlayed there as an address, which points to nothing useful.

Your original example (casting an interface to a chunk of data) is different, to me, than what Zahlman's example does; he interprets an arbitrary block of data as data of the class (that it does not happen to own, and thus doesn't clean up in the destructor). Your original example interprets a block of data as the class itself, which is what I see no sane use for. Yet you say his is the behavior you want. So I'm really lost.
Kest
Kest
Quote:
Original post by jpetrie
Quote:

Is there any way to prevent an object's destructor from being called?

Sure:
1) Don't create it, or
2) Don't delete it.

Deleting it wouldn't be an option since it's declared on the stack. I would want to avoid the automatic calling of the destructor when it goes out of scope. Option 1 was my initial idea, which obviously isn't the best way to approach it.

Quote:
Option (1) involves (beside the obvious) using placement new.

unsigned char *buffer = new buffer[sizeof(T)];T*             t      = new(buffer) T();  // must call t->~T() to end the lifetime of t.  // this will not call the destructor automatically:  delete[] buffer;

I didn't realize placement new would result in the destructor not being called. But as I mentioned with my own error, the buffer itself won't work as a memory pool for T. T would need to rest on the same memory as a char*. Such as..

const char *temp = buffer + offset;
T* t = new(&temp) T();

So temp and T share the same memory location.

Quote:
This is bad, too, obviously, but it might be forgivable if you are wrapping this kind of thing up.

That's the thing. It's difficult to wrap the "const char*" stack declaration as part of the class. I guess one could be allocated and deleted, but that sounds like a real mess (it would have to be casted back to a char* to delete it).

Quote:
Thing is, if you want the "casted instance" to take on the data of the buffer (assuming you got alignment correct), how did you get that data there in the first place? Did you manually write it there? Did you read it from a file?

The data is the code of a text script object. I want to attach my string interface to it to use it's already existing functions to clean the comments and extra spacing from the script code after it's loaded. I need to apply the interface to specific offsets, though. Simply referencing the beginning of the text block won't help much.

Quote:
(why are you not using std::string?).

No particular reason. I started way back in the day with my own wrapper class, and haven't had a reason to switch. Does it have functionality that would help in this type of situation?

Quote:
Your original example (casting an interface to a chunk of data) is different, to me, than what Zahlman's example does; he interprets an arbitrary block of data as data of the class (that it does not happen to own, and thus doesn't clean up in the destructor). Your original example interprets a block of data as the class itself, which is what I see no sane use for. Yet you say his is the behavior you want. So I'm really lost.

Look back over my post with "My example code after the "..." is incorrect". That was the part I was referring to. Sorry about that.
jpetrie
jpetrie
Quote:

No particular reason. I started way back in the day with my own wrapper class, and haven't had a reason to switch. Does it have functionality that would help in this type of situation?


Yes. You should switch.


Quote:

The data is the code of a text script object. I want to attach my string interface to it to use it's already existing functions to clean the comments and extra spacing from the script code after it's loaded.


Then the solution you seem to want is highly overcomplex, overengineers, and unsafe. This is a fairly simple operation.

const char   *rawData = /* raw script code data; null-terminated string */;std::string  codeString(rawData);  /* manipulate codeString here      methods such as std::string::find, substr, erase, et cetera      will provide functionality for trimming whitespace and comments     and so on. Boost's string_algo and regex libraries that operate     on strings provide even more robust options */  /* now, you can use codeString.c_str() to get back a const char* for     the manipulated string, if you need to pass a const char* to a      legacy API or something. Or you can continue to use codeString     as-is, which is probably better */


There's really no need to try and do this manipulation "in place" on a string (char*) buffer -- you'll have serious issues related to needing to change the size of the string, either inserting embedded nulls or adding extra data (which you can't do without resizing the buffer anyway).

There's no need to worry about the "performance overhead" of creating a new string either, or of letting it get destructed at the end of its use.

Even if you don't, for some reason, want to use std::string and instead want to keep using your own, surely you have a constructor for your::string that takes a const char*, right? That constructor should be making a complete copy of the string data pointed to by the const char* parameter and storing that data internally.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.