I would like to know if it is possible to pass a declared type (in this case a record) to my function. I would not even ask if it was not for the SizeOf()
function, because it can take a type as a parameter.
I'm translating code from C and I would like to keep it as close to the original as possible. The C program declares PushArray and PushStruct as macros. Since Delphi does not have macro support, I'm trying to turn them into functions.
I've googled this a bit and it seems that I could possible use generic types.
Like function PushStruct<T>(Arena : Pmemory_arena; dtype : <T>)
but you can only use this 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 original C 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);
}
The C code isn't passing a type to the function. The pre-processor is expanding the macro and computing the size. You can see that from the prototype of the function:
void *PushSize_(memory_arena *Arena, memory_index Size)
Since you don't have macros in Delphi, you cannot arrange a direct translation. Personally, if it were me I would not attempt to match the C code exactly. I'd pass the size and leave it to the caller to use SizeOf
. I don't think that's a terrible burden. It still leaves you with something very close to a literal translation – all you are missing is the convenience macros.
If you wanted to use generics, you could do so, but it would require you to use a static method. For instance:
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 would look 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'd know what name to use instead of TMyClass
!
I'm not convinced that generics are a good fit here because I guess you are wanting as literal a translation as possible. I would not choose to use generics in this scenario.
You could "expand the macros" by simply declaring your own Push* funtions for each record 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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With