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 шт):
Немного додела своё собственное решение. Теперь 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