Cсылки в Delphi

Как известно в Delphi нету таких же ссылок как с++ типа & (ака lvaleu reference). Но в Delphi XE есть дженерики и перегрузка операторов для record. На их основе я своял некое подобие ссылки.

(понадобилось мне это по той причине, что в одной очень старой системе, в которой я сейчас работаю, кроме трудностей с объемным легаси-кодом, есть, как мне кажется, критический дефект в зависимостях между модулями. Один из модулей общей библиотеки, которая не является частью моего project, в разделе uses прописан модуль, который мне, по большому счету, не нужен, но который обязан быть теперь в моём проекте. Когда-то, видимо, оба они являлись частью модулей программ этой системы, но потом один из них перекочевал во внешнюю ссылку, так как его код стал общим для всех программ, но зависимости не были приведены в порядок. И теперь каждая программа обязана содержать модуль с определённым именем и с несколькими переменными, которые самой программой могут и не использоваться. Просто взять и переписать и перекомпилировать десятки программ пока не вариант. С некоторыми вообще не факт, что получится разобраться. Короче - сложно. Но я решил попробовать пока оставить задел на будущее, формировать зависимости между модулями с учётов фактических зависимостей по логике, а тот модуль, что мне, как бы теперь не нужен, пока оставить, но сделать в нём заглушки на переменные в виде ссылок, которые просто "прокидывают" значения из новых переменных)

unit sibReference;

interface

type
  ref<T> = record
    ptr: ^T;

    class operator Initialize(out Dest: ref<T>);
    class operator Implicit(const [ref] Value: T): ref<T>;
    class operator Implicit(const [ref] Value: ref<T>): T;

(*  :=  *) class operator Assign(var Dest: ref<T>; const [ref] Src: ref<T>);
  end;

  string_ref = record
    ptr: ^string;

    class operator Initialize(out Dest: string_ref);
    class operator Implicit(const [ref] Value: string): string_ref;
    class operator Implicit(const [ref] Value: string_ref): string;

(*  :=  *) class operator Assign(var Dest: string_ref; const [ref] Src: string_ref);

(*  +   *) class operator Add(const [ref] a: string_ref; const [ref] b: string_ref): string;

(*  =   *) class operator Equal             (const [ref] a: string_ref; const [ref] b: string_ref): Boolean;
(*  <>  *) class operator NotEqual          (const [ref] a: string_ref; const [ref] b: string_ref): Boolean;
(*  >   *) class operator GreaterThan       (const [ref] a: string_ref; const [ref] b: string_ref): Boolean;
(*  >=  *) class operator GreaterThanOrEqual(const [ref] a: string_ref; const [ref] b: string_ref): Boolean;
(*  <   *) class operator LessThan          (const [ref] a: string_ref; const [ref] b: string_ref): Boolean;
(*  <=  *) class operator LessThanOrEqual   (const [ref] a: string_ref; const [ref] b: string_ref): Boolean;

  end;

implementation

{ ref<T> }

class operator ref<T>.Initialize(out Dest: ref<T>);
begin
  Dest.ptr := nil;
end;

class operator ref<T>.Implicit(const [ref] Value: T): ref<T>;
begin
  Result.ptr := @Value;
  Exit;
end;

class operator ref<T>.Implicit(const [ref] Value: ref<T>): T;
begin
  Result := Value.ptr^;
end;

class operator ref<T>.Assign(var Dest: ref<T>; const [ref] Src: ref<T>);
begin
  if Dest.ptr = nil then
    Dest.ptr  := Src.ptr
  else
    Dest.ptr^ := Src.ptr^;
end;

{ string_ref }

class operator string_ref.Initialize(out Dest: string_ref);
begin
  Dest.ptr := nil;
end;

class operator string_ref.Implicit(const [ref] Value: string): string_ref;
begin
  Result.ptr := @Value;
end;

class operator string_ref.Implicit(const [ref] Value: string_ref): string;
begin
  Result := Value.ptr^;
end;

class operator string_ref.Assign(var Dest: string_ref; const [ref] Src: string_ref);
begin
  if Dest.ptr = nil then
    Dest.ptr  := Src.ptr
  else
    Dest.ptr^ := Src.ptr^;
end;

class operator string_ref.Add(const [ref] a: string_ref; const [ref] b: string_ref): string;
begin
  Result := a.ptr^ + b.ptr^;
end;

class operator string_ref.Equal(const [ref] a: string_ref; const [ref] b: string_ref): Boolean;
begin
  Result := a.ptr^ = b.ptr^;
end;

class operator string_ref.NotEqual(const [ref] a: string_ref; const [ref] b: string_ref): Boolean;
begin
  Result := a.ptr^ <> b.ptr^;
end;

class operator string_ref.GreaterThan(const [ref] a: string_ref; const [ref] b: string_ref): Boolean;
begin
  Result := a.ptr^ > b.ptr^;
end;

class operator string_ref.GreaterThanOrEqual(const [ref] a: string_ref; const [ref] b: string_ref): Boolean;
begin
  Result := a.ptr^ >= b.ptr^;
end;

class operator string_ref.LessThan(const [ref] a: string_ref; const [ref] b: string_ref): Boolean;
begin
  Result := a.ptr^ < b.ptr^;
end;

class operator string_ref.LessThanOrEqual(const [ref] a: string_ref; const [ref] b: string_ref): Boolean;
begin
  Result := a.ptr^ <= b.ptr^;
end;

end.

Потыкал, потестил, посмотрел работу в отладчике - вроде ок. Вот пример использования.

var
    s1, s2: string;
    r1, r2: string_ref;
begin
    s1 := '00000';
    s2 := '11111';
    r1 := s1;
    r2 := s2;
    ShowMessage(s1 + s2); // -> 0000011111
    ShowMessage(r1 + r2); // -> 0000011111

    s1 := '_____';
    ShowMessage(s1 + s2); // -> _____11111
    ShowMessage(r1 + r2); // -> _____11111

    r2 := '+++++' + s2;
    ShowMessage(s1 + s2); // -> _____+++++11111
    ShowMessage(r1 + r2); // -> _____+++++11111

    s1 := r2;
    ShowMessage(s1 + s2); // -> +++++11111+++++11111
    ShowMessage(r1 + r2); // -> +++++11111+++++11111
end;

Конечно, не плюсовые ссылки. Но всёже. Но...
Я оооочень сомневаюсь, что я первый, кому пришла в голову такая идея. Наверняка уже есть такие решения от людей более сведущих чем я, и, соответственно, решения более проработанные, продуманные (например, умеющее правильно работать с временными объектами, продливая врямя их жизни, опять же, как && в плюсах). Вдруг кому попадалось. Я был бы признателен за ссылку.


Ответы (1 шт):

Автор решения: sibedir

Немного додела своё собственное решение. Теперь ref хранит не только ссылку, но и состояние. Если он - это временная ссылка, то он сам создаёт объект и сам его удаляет перед собственным удалением. "Недочёты" этого решения связаны с тем, что в Delphi нельзя перегружать функции только по var и const, оставляя сами типы аргументов такими же.

unit sibReference;

interface

type
  ref<T> = record
    ptr: ^T;
    is_temporary: Boolean; // временная ссылка является так же владеющей

    procedure Join(var Value: T);
    class function Create(var Value: string): ref<T>; static;

    class operator Initialize(out Dest: ref<T>);
    class operator Finalize (var Dest: ref<T>);
    class operator Implicit(Val: T): ref<T>;
    class operator Implicit(var Ref: ref<T>): T;

(*  :=  *) class operator Assign(var Dest: ref<T>; var Src: ref<T>);
  end;

  string_ref = record
    ptr: ^string;
    is_temporary: Boolean;

    procedure Join(var Value: string);
    class function Create(var Value: string): string_ref; static;

    class operator Initialize(out Dest: string_ref);
    class operator Finalize (var Dest: string_ref);
    class operator Implicit(Val: string): string_ref;
    class operator Implicit(var Ref: string_ref): string;

(*  :=  *) class operator Assign(var Dest: string_ref; var Src: string_ref);

(*  +   *) class operator Add(var a: string_ref; var b: string_ref): string; overload;
(*  +   *) class operator Add(var a: string_ref; var b: string    ): string; overload;
(*  +   *) class operator Add(var a: string    ; var b: string_ref): string; overload;

(*  =   *) class operator Equal             (var a: string_ref; var b: string_ref): Boolean;
(*  <>  *) class operator NotEqual          (var a: string_ref; var b: string_ref): Boolean;
(*  >   *) class operator GreaterThan       (var a: string_ref; var b: string_ref): Boolean;
(*  >=  *) class operator GreaterThanOrEqual(var a: string_ref; var b: string_ref): Boolean;
(*  <   *) class operator LessThan          (var a: string_ref; var b: string_ref): Boolean;
(*  <=  *) class operator LessThanOrEqual   (var a: string_ref; var b: string_ref): Boolean;

  end;

implementation

uses
  SysUtils;

{ ref<T> }

procedure ref<T>.Join(var Value: T);
begin
  // Если ссылка еще не связана
  if ptr = nil then begin
    // то связываем её
    ptr := @Value;
    is_temporary := False;
    Exit;
  end;
  // иначе ошибка
  raise Exception.Create('попытка пересвязывания ссылки');
end;

class function ref<T>.Create(var Value: string): ref<T>;
begin
  Result.Join(Value);
end;

class operator ref<T>.Initialize(out Dest: ref<T>);
begin
  Dest.ptr := nil;
end;

class operator ref<T>.Finalize(var Dest: ref<T>);
begin
  // Если сстылка временная, то удаляем данные перед собственным уничтожением
  if Dest.is_temporary then Dispose(Dest.ptr);
end;

class operator ref<T>.Implicit(Val: T): ref<T>;
begin
  // При попытке привести Т к ref создаётся временная ссылка.
  // Это продлевает время жизни ссылки до конца выражения,
  // в котором она учавствует.
  with Result do begin
    New(ptr);
    ptr^ := Val;
    is_temporary := True;
  end;
end;

class operator ref<T>.Implicit(var Ref: ref<T>): T;
begin
  // Обратное приведение из Ref к Т тривиально
  Result := Ref.ptr^;
end;

class operator ref<T>.Assign(var Dest: ref<T>; var Src: ref<T>);
begin
  // Если ссылка слева от ':=' уже с чем-то связана
  if Dest.ptr <> nil then begin
    // то данные просто копируются в объект, с которым связан Dest
    Dest.ptr^ := Src.ptr^;
    Exit;
  end;
  // иначе ошибка
  raise Exception.Create('попытка записи в несвязанную ссылку');
end;

{ string_ref }

procedure string_ref.Join(var Value: string);
begin
  if ptr = nil then begin
    ptr := @Value;
    is_temporary := False;
    Exit;
  end;
  raise Exception.Create('попытка пересвязывания ссылки');
end;

class function string_ref.Create(var Value: string): string_ref;
begin
  Result.Join(Value);
end;

class operator string_ref.Initialize(out Dest: string_ref);
begin
  Dest.ptr := nil;
end;

class operator string_ref.Finalize(var Dest: string_ref);
begin
  if Dest.is_temporary then Dispose(Dest.ptr);
end;

class operator string_ref.Implicit(Val: string): string_ref;
begin
  with Result do begin
    New(ptr);
    ptr^ := Val;
    is_temporary := True;
  end;
end;

class operator string_ref.Implicit(var Ref: string_ref): string;
begin
  Result := Ref.ptr^;
end;

class operator string_ref.Assign(var Dest: string_ref; var Src: string_ref);
begin
  if Dest.ptr <> nil then begin
    Dest.ptr^ := Src.ptr^;
    Exit;
  end;

  raise Exception.Create('попытка записи в несвязанную ссылку');
end;

class operator string_ref.Add(var a: string_ref; var b: string_ref): string;
begin
  Result := a.ptr^ + b.ptr^;
end;

class operator string_ref.Add(var a: string_ref; var b: string): string;
begin
  Result := a.ptr^ + b;
end;

class operator string_ref.Add(var a: string; var b: string_ref): string;
begin
  Result := a + b.ptr^;
end;

class operator string_ref.Equal(var a: string_ref; var b: string_ref): Boolean;
begin
  Result := a.ptr^ = b.ptr^;
end;

class operator string_ref.NotEqual(var a: string_ref; var b: string_ref): Boolean;
begin
  Result := a.ptr^ <> b.ptr^;
end;

class operator string_ref.GreaterThan(var a: string_ref; var b: string_ref): Boolean;
begin
  Result := a.ptr^ > b.ptr^;
end;

class operator string_ref.GreaterThanOrEqual(var a: string_ref; var b: string_ref): Boolean;
begin
  Result := a.ptr^ >= b.ptr^;
end;

class operator string_ref.LessThan(var a: string_ref; var b: string_ref): Boolean;
begin
  Result := a.ptr^ < b.ptr^;
end;

class operator string_ref.LessThanOrEqual(var a: string_ref; var b: string_ref): Boolean;
begin
  Result := a.ptr^ <= b.ptr^;
end;

end.

any_ref := value; НЕ СВЯЖЕТ any_ref с value!
Если any_ref не связан ни с одним объектом, := - это ошибка.
Если же any_ref уже был связан, то данные из value просто скопируются в объект, с которым any_ref связан. Для связи ссылки нужно использовать Join или Create

Использование:

var
  s1, s2, tmp: string;
  r1, r2: string_ref;
begin
  s1 := '1111';
  s2 := '2222';

  r1 := string_ref.Create(s1);
  r2.Join(s2);

  Memo1.Lines.Add('============================================');
  Memo1.Lines.Add('s1 {' + IntToHex(Integer(@s1)   ) + '} = ' + s1);
  Memo1.Lines.Add('r1 {' + IntToHex(Integer(r1.ptr)) + '} = ' + r1);
  Memo1.Lines.Add('s2 {' + IntToHex(Integer(@s2)   ) + '} = ' + s2);
  Memo1.Lines.Add('r2 {' + IntToHex(Integer(r2.ptr)) + '} = ' + r2);

  r1 := '3333';
  r2 := '4444' + s2;
  Memo1.Lines.Add('-----------------------');
  Memo1.Lines.Add('s1 {' + IntToHex(Integer(@s1)   ) + '} = ' + s1);
  Memo1.Lines.Add('r1 {' + IntToHex(Integer(r1.ptr)) + '} = ' + r1);
  Memo1.Lines.Add('s2 {' + IntToHex(Integer(@s2)   ) + '} = ' + s2);
  Memo1.Lines.Add('r2 {' + IntToHex(Integer(r2.ptr)) + '} = ' + r2);

  r1 := r1 + '5555' + r2;
  r2 := (Length(r1 + r2).ToString);
  Memo1.Lines.Add('-----------------------');
  Memo1.Lines.Add('s1 {' + IntToHex(Integer(@s1)   ) + '} = ' + s1);
  Memo1.Lines.Add('r1 {' + IntToHex(Integer(r1.ptr)) + '} = ' + r1);
  Memo1.Lines.Add('s2 {' + IntToHex(Integer(@s2)   ) + '} = ' + s2);
  Memo1.Lines.Add('r2 {' + IntToHex(Integer(r2.ptr)) + '} = ' + r2);

  tmp := r1;
  r1 := r2;
  r2 := r2 + tmp;
  Memo1.Lines.Add('-----------------------');
  Memo1.Lines.Add('s1 {' + IntToHex(Integer(@s1)   ) + '} = ' + s1);
  Memo1.Lines.Add('r1 {' + IntToHex(Integer(r1.ptr)) + '} = ' + r1);
  Memo1.Lines.Add('s2 {' + IntToHex(Integer(@s2)   ) + '} = ' + s2);
  Memo1.Lines.Add('r2 {' + IntToHex(Integer(r2.ptr)) + '} = ' + r2);

end;

Вывод:

============================================
s1 {0153EEA8} = 1111
r1 {0153EEA8} = 1111
s2 {0153EEA4} = 2222
r2 {0153EEA4} = 2222
-----------------------
s1 {0153EEA8} = 3333
r1 {0153EEA8} = 3333
s2 {0153EEA4} = 44442222
r2 {0153EEA4} = 44442222
-----------------------
s1 {0153EEA8} = 3333555544442222
r1 {0153EEA8} = 3333555544442222
s2 {0153EEA4} = 24
r2 {0153EEA4} = 24
-----------------------
s1 {0153EEA8} = 24
r1 {0153EEA8} = 24
s2 {0153EEA4} = 243333555544442222
r2 {0153EEA4} = 243333555544442222
→ Ссылка