Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi - How to pass a 'Type' as a parameter

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);
}
like image 738
Stefan Badenhorst Avatar asked Mar 17 '23 08:03

Stefan Badenhorst


2 Answers

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.

like image 163
David Heffernan Avatar answered Mar 24 '23 20:03

David Heffernan


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;
like image 28
Ondrej Kelle Avatar answered Mar 24 '23 21:03

Ondrej Kelle