Is Oxygene Island ABI with C struct, or C++ pure abstarct class v-table?

Jumping in because I have a similar problem, using some C++ classes in Delphi this way.

FooBarType = function(aSelf: ^IMyCplusplusDataInterface; aData: ^Data): Integer;
IMyCplusplusDataInterface = public record 
         FooBar: FooBarType;
end;

This is from Island to C++, right?
how to use in the direction from C++ to Island?

Ah!!

I suddenly realized the rationale of your suggested approach. It is really a “COM-like” or “COM-Lite” way.

The Island-side record acts like an emulated V-Table.

On the C++ side, that Struct has an implicit V-Table pointer in its memory layout.

So, I should really return a pointer of the Island record, and assign that pointer to the C++ Struct (Which actually makes a copy of the Island record pointer, and assigns the copy to that C++ Struct’s V-Table pointer).

This also means the life cycle of that Island record can not be ephemeral; it should be of a global nature, i.e the V-table must be there.

Is my understanding correct?

It shouldn’t copy it no, it should use the pointer. For lifetime: well normally we DO use com, which has the AddRef, Release methods that handle lifetime (those are the IUnknown methods, the other side cleans it up).

What I meant is - in the C++ Struct’s memory layout, its v-table pointer is assigned with a value that is actually the value of the Island record v-table pointer.

The size of that C++ Struct is just the same size as its V-table pointer (since it has no data member).

Correct?

Ah like that. Normally it’s double indirect actually:

MyRecord = record
public
  fVMT: ^MyRecordVMT;
  fFirstfield: Integer;
end;

MyRecordVmt = record
public
  fVMTMethod1: Method1Type;
end;

There’s only 1 vmt, and multiple instance of ^MyRecord .

1 Like

Same way basically, but they have to be virtual to get an entry in the VMT.

Sorry Carlo, I don’t get it:

Look at this Delphi Code:

unit OCCTypes;

interface

type
 TOccShapeManager = class abstract
  public
    procedure ClearSolids; virtual; stdcall; abstract;
  end;

  function getOccManager: TOccShapeManager;  cdecl; external   'DOCC.DLL';
  function releaseOccManager : integer ; cdecl; external   'DOCC.DLL';

implementation

end.

This will compile and work,
and now Island?

namespace OCCIsland;
type
  [CallingConvention(CallingConvention.Stdcall)]
  ClearSolidsType = public method (aSelf: ^TOccShapeManager);// virtual;

  TOccShapeManager = public  record
  public
    ClearSolids  : ClearSolidsType; //virtual;
 
  end;

{$GLOBALS ON}

  [DllImport('DOCC.DLL')]
  [CallingConvention(CallingConvention.Cdecl)]
  method getOccManager: TOccShapeManager; external;

  [DllImport('DOCC.DLL')]
  [CallingConvention(CallingConvention.Cdecl)]
  method releaseOccManager: Integer; external;
end.

Where do I need the virtual?

Wait, your’e calling delphi code over a dll boundary? That’s quite different. Do you have the c++ side of this for me?

Yes I can call Delphi Code from C++ and vice versa.
Behause they are VMT compatible as long they are declared as
pure abstract classes like:

#define STDCALL __stdcall

#pragma pack(push,4)

class OccShapeManagerIntf

{

public:

virtual void STDCALL ClearSolids(void) = 0 ;

virtual void STDCALL AddInt(int value) = 0;

};

//-- var, const, procedure ---------------------------------------------------

EXTERN_C OCCTYPES_API OccShapeManagerIntf* _cdecl getOccManager(void);

EXTERN_C OCCTYPES_API int _cdecl releaseOccManager(void);

#pragma pack(pop)

//-- end unit ----------------------------------------------------------------

#endif // Occtypes

It works in 32 and 64 Bit

hrmm. I don’t think it’s a good idea to mix our class with this no (the vmt will not match); but a record of sorts could work:

namespace OCCIsland;
type
  [CallingConvention(CallingConvention.Stdcall)]
  ClearSolidsType = public method (aSelf: ^TOccShapeManager);// virtual;
  TOccShapeManagerPtr = ^TOccShapeManager;
  TOccShapeManager = public  record
  public
    Vmt: ^TOccShapeManagerVmt;
  end;
  TOccShapeManagerVmt = public record 
  public
    ClearSolids  : ClearSolidsType; // << like this.
  end;

{$GLOBALS ON}

  [DllImport('DOCC.DLL')]
  [CallingConvention(CallingConvention.Cdecl)]
  method getOccManager: TOccShapeManagerPtr; external;

  [DllImport('DOCC.DLL')]
  [CallingConvention(CallingConvention.Cdecl)]
  method releaseOccManager: Integer; external;
end.

It will not work.
But don’t spend more time in it, I will wrap the C++ classes in a Com Interface for now

Oke.

How soon can I have this Alignment fix be available? Sorry for being nagging… I cann’t help waiting…
Any estimated timeline?

not every fix can be done within 24 hours, and our teams do have a lot on their plates. please be a bit more patient. with literally any other compiler vendor, you’d be looking at a timeline of months, if not years, for a fix like this.

Understand :slight_smile: and please forgive my nagging

I blame you guys - you made Oxygene a beautiful language that made me excited and just can’t wait to get started :stuck_out_tongue_winking_eye:

1 Like

I’ve been checking but I’m having a hard time making it do consistent things in c++ for:

   #pragma pack(push, 8) // 8-byte packing

as far as I can tell, it will align it on at most 8 bytes, nothing more. But that’s pretty much a given in our compiler…

the only way you can get > 8 bytes is with a type that’s larger than 8 bytes, which we don’t have. So the current behavior should already work?

The C++ alignment pack logic is best described here
https://docs.microsoft.com/en-us/cpp/build/reference/zp-struct-member-alignment?view=vs-2019

Basically, a struct data member AFTER the first member will be aligned at a boundary that is the minimum of that data member’s size, or the designated byte number (e.g , 8)

For example, as in the StackOverflow sample (which I don’t think they explain the steps clear enough)

struct Pack8Data {
   short int a;
   double b;
   short int c;
   int d;
}

b is aligned at min (8,8) = 8, hence offset = 8
c is aligned at min (2,8) = 2, hence offset = 16
d is aligned at min (4,8) = 4, hence offset = 20

SizeOf(Pack8Data) = 24

As we can see, the packing rule is well defined, at least for MSVC compiler.

Is this the same rule implemented by Island compiler? If so, then we are all good.

In any case, even Island does a default 8 byte pack alignment, it is still nice to allow the user to change it to a different value something like C++ PRAGMA or the C++ compiler switch /Zp.

yeah it is (but at least should get you going now?). Above is what we do.

Yes. I can get started using existing Island.
Thank you for the confirmation and cheers. :+1:

```
namespace OCCIsland;
type
  [CallingConvention(CallingConvention.Stdcall)]
  ClearSolidsType = public method (aSelf: ^TOccShapeManager);// virtual;

  TOccShapeManager = public  record
  public
    ClearSolids  : ClearSolidsType; //virtual;
 
  end;
```

How should I assign a function to ClearSolidsType?

method ClearSolidsFunc(aSelf: ^TOccShapeManager);
begin 
...
end;

If I set ClearSolids := ClearSolidsFunc; I will get the error saying "E397: No overloaded global function “ClearSolidsFunc” with 0 parameters in namespace xxx

What is the property way to assign function pointer?