[Delphi] Classes and structures not compatible

Started by
6 comments, last by Devilogic 17 years, 11 months ago
I have a C++ structure in memory which I need to use in Delphi. I have the pointer to the structure, but the problem is that the address of the C++ structure is the address of the first property of that structure. Eg.

struct sStruct
{
   dword dwProperty;
   ...
}

sStruct tmp;
&tmp is equal to &(tmp.dwProperty). However, in Delphi the equivilent data type is:

sStruct = class(TObject)
begin
   dwProperty: DWORD;
   ...
end;

tmp: sStruct;
@(tmp.dwProperty) is 4 bytes further along than tmp (@tmp is not needed as tmp is a pointer). I have investigated, and record types behave like C++ structs (same memory address, although C++ structs seem to actually be like Classes in that they allow methods, constructors and destructors), but I have to use classes, as I have circular references, ie:

sStructA = class(TObject)
begin
   pObject: sStructB;
   ...
end;

sStructB = class(TObject)
begin
   pObject: sStructA;
   ...
end;
That wouldn't be possible with records. Also, I would like to have constructors and destructors to initialise the data, but this is less essential. The only solution I can think of at the moment is to go through and manually decrease all the object pointers by 4, but that would be highly inconvenient. Is there any way to make Delphi objects able to point to C++ structures? Thanks for your time. [Edited by - hpesoj on May 23, 2006 2:08:05 PM]
Joseph Thomson - CLS Productions
Advertisement
Trying to make assumptions about the binary layout of language-specific structures is not recommended, for example C++ doesn't mandate how classes should be implemented memory-wise (correct me if I'm wrong) so it may differ among compilers. A better way to share data would be to pass a pointer to an array of byte-encoded data, about which there can be no misunderstandings. Another idea is to write C-style wrapper code around your C++ objects, so it can easily be used from other languages. Hope this helps.
Quote:Original post by hpesoj
However, in Delphi the equivilent data type is:


No, it's not.

You are comparing a stand alone data structure in C to a Object in Delphi. They are not the same.

The Delphi version of a struct is called a record

TStruct = record   dwProperty: DWORD;end;


If you are using more then one member you might want to look into packed structs/records (if they are not packed, your compiler/settings will decide how best to space multiple members, which usually means they will be 32bit aligned).

So to review:

C++        Delphi-----------------int        integer{...}      begin...endstruct     recordclass      class
Michalson, in C++ struct and class keywords are almost interchangeable.

So, more correctly:
	C++        Delphi-----------------int        integer{...}      begin...endPOD struct recordclass      class


where POD stands for "plain old data", a technical term in the C++ standard.

POD struct/classes are, basically, classes/structs whose pointers point at the first data member, and have some other rudimentary memory-layout guarantees.

The rest of your advice is sound.
// c/c++
struct sStruct {
dword dwProperty;
};

// delphi
sStruct = packed record
dwProperty: DWORD:
end;

You can pass these structures back in forth. For example if you have a dll created in c/c++ and an exported routine that expects a record or pointer of this type, you can safely pass this from Delphi to the dll. The thing to insure is that the data alignments for the record structures are the same.

In the case of sharing classes, since Delphi supports COM and if all the methods are virtual they share the same binary layout in memory with that of a c++ class with all virtual methods. With this in mind you can create an all method virtual class in c/c++ and using a factory, return a pointer to that class instance. If you create the same class in Delphi, you will be able to use that class in Delphi. You have to make sure your factory will also destroy the class also. If it's destroyed on the opposite side of creation, bad things will happen.

I use all these techniques in my game engine GameVision SDK. Feel free to contact me if you have more questions or concerns.

Jarrod Davis
Technical Director

FaceBook | Twitter | YouTube
"It's a beautiful thing to have power,
choice and options."

Quote:Original post by Prototype
Trying to make assumptions about the binary layout of language-specific structures is not recommended, for example C++ doesn't mandate how classes should be implemented memory-wise (correct me if I'm wrong) so it may differ among compilers. A better way to share data would be to pass a pointer to an array of byte-encoded data, about which there can be no misunderstandings. Another idea is to write C-style wrapper code around your C++ objects, so it can easily be used from other languages. Hope this helps.


Well, so long as I use the same compiler all the time there shouldn't be a problem, however I see your point. Using structures (or similar) is faster than reading the whole thing byte by byte, though that would be possible and probably quite feasable if you wanted to read/write only one part of the data. It is of course a lot more convenient to be able to refer to a piece of data as something like 'Object.Array[3].SubObject.Property', than having to scan though the data and follow pointers, but you can't have everything I guess. Note that the data is being written by a separate application, so I only really have control over the Delphi side of things. I have been converting the C++ header files for loading the data, but have run into a wall with this.

Quote:Original post by Michalson
No, it's not.

You are comparing a stand alone data structure in C to a Object in Delphi. They are not the same.

The Delphi version of a struct is called a record



TStruct = record

dwProperty: DWORD;

end;



If you are using more then one member you might want to look into packed structs/records (if they are not packed, your compiler/settings will decide how best to space multiple members, which usually means they will be 32bit aligned).

So to review:



C++ Delphi

-----------------

int integer

{...} begin...end

struct record

class class


Well, kind of. Like I said, Delphi records appear to have the same internal data structure as C++ structures, but in C++ (or at least VC++) structures can have methods, and seem to be handled exactly like classes (I'm not a C++ guru). Also, as I said, I need circular references. Circular references with records would not work if it were actually allowed by the Delphi compiler (forward declarations of records are forbidden). The solution is to use classes.

I will look into the packed records thing, thanks.

I have found this article that seems to deal with the problem at hand. It would require me to persuade the C++ developer to change his program though. I am not very well grounded in C++ as I said, so if anyone could summarise what this would entail that'd be good (the C++ structures have a constructor and destructor, and no members).

http://rvelthuis.de/articles/articles-cppobjs.html

Thanks for your replied both.
Joseph Thomson - CLS Productions
C++ "plain old data" (POD) structs and classes have the same binary layout. And probably line up with Delphi records.

C++ POD structs and classes can have methods. They cannot have constructors, destructors, or virtual (overrideable) methods. They cannot contain references (but can contain pointers). The C++ standard describes what can go into a POD struct.

C++ non-POD structs and classes have very little in the way of binary layout guarentees. Practically, they often consists of a pointer to their virtual function table followed by their data. But this varies.
Quote:Original post by hpesoj
Well, kind of. Like I said, Delphi records appear to have the same internal data structure as C++ structures, but in C++ (or at least VC++) structures can have methods, and seem to be handled exactly like classes (I'm not a C++ guru). Also, as I said, I need circular references. Circular references with records would not work if it were actually allowed by the Delphi compiler (forward declarations of records are forbidden). The solution is to use classes.

Actually you could still use records, you would only need to replace "circular references to classes" (actually compiled into pointers by the compiler) with explicit pointers. It would look kind of like this:

type  PStructA = ^TStructA;  PStructB = ^TStructB;  TStructA = record    B: PStructB;    ...  end;  TStructB = record    A: PStructA;    ...  end;

CAUTION: In case you decide to use pointers be sure to call New(...) and Dispose(...) on the records pointed to by PStructX (unless those records were defined among static variables of course), otherwise you will get overflows, leaks and other kinds of nasty things ;)

This topic is closed to new replies.

Advertisement