Delphi - How to pass "Type" as parameter
I would like to know if the declared type (in this case the record) can be passed to my function. I wouldn't even ask if it wasn't for a function SizeOf()
, because it can take a type as a parameter.
I am translating the code from C and I would like to keep it as close to the original as possible. Program C declares the PushArray and PushStruct macros. Since Delphi does not have macro support, I am trying to turn them into functions.
I searched for this version a bit and it seems to me that I can use generic types. Like function PushStruct<T>(Arena : Pmemory_arena; dtype : <T>)
, but you can only use it in an OOP type application.
function PushSize_(Arena : Pmemory_arena; Size : memory_index) : pointer;
begin
Assert((Arena^.Used + Size) <= Arena^.Size);
Result := Arena^.Base + Arena^.Used;
Arena^.Used := Arena^.Used + Size;
end;
function PushStruct(Arena : Pmemory_arena; dtype : ?) : pointer;
begin
result := PushSize_(Arena, sizeof(dtype));
end;
function PushArray(Arena : Pmemory_arena; Count: uint32; dtype : ?) : pointer;
begin
result := PushSize_(Arena, (Count)*sizeof(dtype))
end;
Here is the C source code:
#define PushStruct(Arena, type) (type *)PushSize_(Arena, sizeof(type))
#define PushArray(Arena, Count, type) (type *)PushSize_(Arena, (Count)*sizeof(type))
void *
PushSize_(memory_arena *Arena, memory_index Size)
{
Assert((Arena->Used + Size) <= Arena->Size);
void *Result = Arena->Base + Arena->Used;
Arena->Used += Size;
return(Result);
}
source to share
The C code does not pass the type to the function. The preprocessor expands the macro and calculates the size. You can see this from the function prototype:
void *PushSize_(memory_arena *Arena, memory_index Size)
Since you don't have macros in Delphi, you cannot arrange for direct translation. Personally, if it was me, I would not try to match the C code exactly. I would pass the size and leave it to the caller to use SizeOf
. I don't think this is a terrible burden. That still leaves you with something very close to literal translation - all you're missing are convenience macros.
If you want to use generics, you can do so, but you need to use a static method to do this. For example:
type
TMyClass = class
class function PushSize(Arena: Pmemory_arena; Size: memory_index): Pointer; static;
class function PushStruct<T>(Arena: Pmemory_arena): Pointer; static;
end;
....
class function TMyClass.PushSize(Arena: Pmemory_arena; Size: memory_index): Pointer;
begin
Result := ....;
end;
class function TMyClass.PushStruct<T>(Arena: Pmemory_arena): Pointer;
begin
Result := PushSize(Arena, SizeOf(T));
end;
If you want to return a typed pointer that looks like this:
type
TMyClass<T> = class
type P = ^ T;
class function PushSize(Arena: Pmemory_arena; Size: memory_index): Pointer; static;
class function PushStruct(Arena: Pmemory_arena): P; static;
end;
....
class function TMyClass<T>.PushSize(Arena: Pmemory_arena; Size: memory_index): Pointer;
begin
Result := ....;
end;
class function TMyClass<T>.PushStruct(Arena: Pmemory_arena): P;
begin
Result := PushSize(Arena, SizeOf(T));
end;
Obviously, you know which name to use instead TMyClass
!
I'm not sure if generics are good here because I am assuming you want a literal translation as possible. I would not choose to use generics in this scenario.
source to share
You can "expand macros" by simply declaring your own Push * functions for each post type as needed:
type
// just guessing here...
PMemoryArena = ^TMemoryArena;
TMemoryArena = record
Base: Pointer;
Used: Cardinal;
Size: Cardinal;
end;
TMemoryIndex = Cardinal;
// 1st example record type
PMyRecord1 = ^TMyRecord1;
TMyRecord1 = record
I1: Integer;
I2: Integer;
end;
// 2nd example record type
PMyRecord2 = ^TMyRecord2;
TMyRecord2 = record
D1: Double;
D2: Double;
end;
function PushSize_(Arena: PMemoryArena; Size: TMemoryIndex): Pointer; inline;
begin
Assert(Arena^.Used + Size <= Arena^.Size);
Result := Pointer(NativeUInt(Arena^.Base) + Arena^.Used);
Inc(Arena^.Used, Size);
end;
function PushMyRecord1(Arena: PMemoryArena): PMyRecord1;
begin
Result := PMyRecord1(PushSize_(Arena, SizeOf(TMyRecord1)));
end;
function PushMyRecord2(Arena: PMemoryArena): PMyRecord2;
begin
Result := PMyRecord2(PushSize_(Arena, SizeOf(TMyRecord2)));
end;
source to share