Как передать общую запись в качестве параметра в функцию TFileStream.Read? - PullRequest
3 голосов
/ 03 июля 2019

У меня есть несколько типов записей для чтения из файла, например

  PDescriptorBlockHeader = ^TDescriptorBlockHeader;
  TDescriptorBlockHeader = packed record

    BlockType: UInt32;
    BlockAttributes: UInt32; // +4
    OffsetToFirstEvent: UInt16; // +8
    OsId: byte; // +10
    OsVersion: byte;
    DisplayableSize: UInt64;  // +12
    FormatLogicalAddress: UInt64; // +20
    SessionId: UInt64; // +28
    ControlBlockID: UInt32; // +36
    StringStorage: MTF_TAPE_ADDRESS; // +40
    OsSpecificData: MTF_TAPE_ADDRESS; // +44
    StringType: byte; // +48
    Reserved: byte; // +49
    HeaderChecksum: UInt16; //+50
  end;

, и я хочу использовать обычную функцию для чтения из файла

type
  TReaderHelper = class
    class procedure ReadToStruct<T:record>(stream: TFileStream; offset: Int64);
  end;

implementation

class procedure TReaderHelper.ReadToStruct<T>(stream: TFileStream; offset: Int64);
var
  rd: integer;
begin
  stream.Position := offset;
  if stream.Position <> offset then
    raise Exception.Create('Seek error');
  rd := stream.Read(T, sizeof(T));
  if rd <> sizeof(T) then
    raise Exception.Create('Read ' + IntToStr(rd) + ' instead of ' + IntToStr(sizeof(T)));
end;

Компилятор выдает ошибку E2571 Type parameter 'T' doesn't have class or interface constraint на rd := stream.Read(T, sizeof(T));.Можно ли передать эту общую запись в качестве параметра в функцию TFileStream.Read?

Ответы [ 2 ]

8 голосов
/ 03 июля 2019

Вы пытаетесь читать прямо на T, который является типом . Вам необходимо предоставить переменную того типа, в которую нужно читать.

type
  TReaderHelper = class
    class procedure ReadToStruct<T: record>(stream: TStream; offset: Int64; out Data: T);
  end;

class procedure TReaderHelper.ReadToStruct<T>(stream: TStream; offset: Int64; out Data: T);
begin
  stream.Position := offset;
  stream.ReadBuffer(Data, sizeof(T));
end;

Вместо того, чтобы предоставлять конкретный класс потока, такой как TFileStream, он более гибок, чтобы предоставить общий класс потока. Это позволяет использовать этот метод с различными реализациями потока.

Исключение поиска, которое вы вызвали, не имеет смысла, потому что можно искать за концом файла. Любые ошибки возникают в последующих действиях чтения или записи.

Другое исключение подходит, но, возможно, проще использовать ReadBuffer и позволить классу потока вызвать исключение в случае, если запрошенный объем данных не может быть прочитан.

7 голосов
/ 03 июля 2019

T представляет тип, а не переменную. Вам нужно передать переменную в Read(). Добавьте выходную переменную в ваш код и прочитайте ее, например:

type
  TReaderHelper = class
    class procedure ReadToStruct<T:record>(stream: TFileStream; offset: Int64: out rec: T);
  end;

implementation

class procedure TReaderHelper.ReadToStruct<T>(stream: TFileStream; offset: Int64; out rec: T);
var
  rd: integer;
begin
  stream.Position := offset;
  if stream.Position <> offset then
    raise Exception.Create('Seek error');
  rd := stream.Read(rec, sizeof(T));
  if rd <> sizeof(T) then
    raise Exception.Create('Read ' + IntToStr(rd) + ' instead of ' + IntToStr(sizeof(T)));
end;

В качестве альтернативы:

type
  TReaderHelper = class
    class function ReadToStruct<T:record>(stream: TFileStream; offset: Int64): T;
  end;

implementation

class function TReaderHelper.ReadToStruct<T>(stream: TFileStream; offset: Int64): T;
var
  rd: integer;
begin
  stream.Position := offset;
  if stream.Position <> offset then
    raise Exception.Create('Seek error');
  rd := stream.Read(Result, sizeof(T));
  if rd <> sizeof(T) then
    raise Exception.Create('Read ' + IntToStr(rd) + ' instead of ' + IntToStr(sizeof(T)));
end;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...