Созлание property array, который работает со структурными
От: nik-tmn Россия  
Дата: 26.03.03 10:57
Оценка:
Hi, All
Подскажите, please, как создать такое свойство (property),к которому можно обращатся так:

pr['1'].x:=1;
pr['2'].y:=pr['1'].x/10;

и т.п.
Пробовал так:


type
TData=packed record
 x,y:single;
 s:string;
end;

TDataEdit=class(TComponent)
...........
private
 procedure SetData(title:string; value:TData);
 function GetData(title:string):TData;
public
 property Dat[title:string]:TData read GetData write SetData;

end;


Проблема в том, что при попытке присваивания (например, DataEdit.Dat['1'].x:=10 при компиляции выскакивает сообщение Cannot assign left side.

Заранее спасибо всем откликнувшимся.
Re: Созлание property array, который работает со структурным
От: Slicer [Wirkwood] Россия https://ru.linkedin.com/in/maksim-gumerov-039a701b
Дата: 26.03.03 11:01
Оценка: 13 (1)
Делай TData классом.

Иначе все логично — небезопасно выставлять половину свойства

Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Re: Созлание property array, который работает со структурным
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 26.03.03 11:20
Оценка:
Здравствуйте, nik-tmn, Вы писали:

NT>type

NT>TData=packed record
NT> x,y:single;
NT> s:string;
NT>end;

Вместо записи надо создавать класс.
Re[2]: Созлание property array, который работает со структур
От: nik-tmn Россия  
Дата: 26.03.03 12:21
Оценка:
Здравствуйте, Mystic, Вы писали:

M>Вместо записи надо создавать класс.

Thanks .
Но, как потом использовать созданный класс? Я хочу сделать как бы хранилище данных, для доступа к которуму достаточно указать название и получить для него все параметры.

Я делал так:

function GetData(title:string):TData;
var i:integer;
begin
 i:=Sk.IndexOf(title);
 if i<>-1 then
  Result:=Data[i];
end;

где
Data: array of TData;
Sk:TStringList,

хранящий названия
Re[3]: Созлание property array, который работает со структур
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 26.03.03 12:42
Оценка: 8 (1)
Здравствуйте, nik-tmn, Вы писали:

NT>Здравствуйте, Mystic, Вы писали:


M>>Вместо записи надо создавать класс.

NT>Но, как потом использовать созданный класс? Я хочу сделать как бы хранилище данных, для доступа к которуму достаточно указать название и получить для него все параметры.

Еще можно передавать указатель на структуру.

Почему не работает то, что хотели вы:
Дело в том, что когда запись является возвращаемым значением, в стеке создается результат функции и происходит копирование записи (также возвращаются целочисленные типы, ...). Поэтому изменение в результате функции никак не могут затронуть тот оригинал, что хранится в классе.

Попытайся так:

NT>function GetData(title:string):PData;

NT>var i:integer;
NT>begin
NT> i:=Sk.IndexOf(title);
NT> if i<>-1 then
NT> Result:=@Data[i];
NT>end;


Сравни
function X1: Integer; // X1 := 5; -- бессмысленно

function X2: PInteger; // X2^ := 5; -- логично
Re[4]: Созлание property array, который работает со структур
От: nik-tmn Россия  
Дата: 26.03.03 14:19
Оценка:
Здравствуйте, Mystic, Вы писали:

M>Попытайся так:

Спасибо! Завтра попробую.
Re[4]: Созлание property array, который работает со структур
От: nik-tmn Россия  
Дата: 27.03.03 04:58
Оценка:
Здравствуйте, Mystic, Вы писали:

M>Попытайся так:


NT>>function GetData(title:string):PData;

NT>>var i:integer;
NT>>begin
NT>> i:=Sk.IndexOf(title);
NT>> if i<>-1 then
NT>> Result:=@Data[i];
NT>>end;

Вроде работает но не так как надо

Код такой:
type
TOriginData_=packed record
SkvName,Kust,Plast:string[20];
sost:boolean;
Bs,IsTgo,OssTgo:single;
tgo:TDateTime;
GRPData:GRP__;

ZgrpInd:double;
TP:string[10];
ro,Ap,mp,Cp,muF,VF,qF:double;
FF:byte;
Mest,PlastGRP:string[20];
TGrp:TDateTime;

end;
POriginData_=^TOriginData_;

TOriginData=class(TComponent)
protected
SkvNm:TStringList;
Data:array of POriginData_;
ZeroData:POriginData_;
procedure SetOriginData(const skvname:string;Value:POriginData_);
function GetOriginData(const skvname:string):POriginData_;
public
constructor Create(AOwner:TComponent); override;
destructor Destroy; override;

property ODWell[const skvname:string]:POriginData_ read GetOriginData write SetOriginData; default;
end;

В конструкторе создается
New(ZeroData);
и присваиваются значения
ZeroData.SkvName:='';
ZeroData.Bs:=0;
и т.д.

procedure TOriginData.SetOriginData(const skvname:string;value:POriginData_);
var i:integer;
begin
i:=SkvNm.IndexOf(AnsiLowerCase(skvname));
if i=-1 then begin
SkvNm.Add(AnsiLowerCase(skvname));
SetLength(Data,SkvNm.Count);
New(Data[SkvNm.Count-1]);
Data[SkvNm.Count-1]:=@ZeroData;
Data[SkvNm.Count-1]:=@Value;
// Data[SkvNm.Count-1]^.SkvName:='test';
// Data[SkvNm.Count-1].SkvName:=AnsiLowerCase(skvname);
end else begin
Data[i]:=@Value;

end;
end;

Проблема заключается в том, что не обнуляются значения при создании нового параметра, а чаще всего в них мусор, хотя затем при записи -чтении все работает.
По-видимому, что-то не так с присвоением ZeroData.
Кто знает подскажите...
Re[5]: Созлание property array, который работает со структур
От: mrhru Россия  
Дата: 27.03.03 05:50
Оценка: 39 (1)
Здравствуйте, nik-tmn, Вы писали:

Mystic правильно посоветовал "Вместо записи надо создавать класс"

type
  // вместо record используем класс
  // мороки гораздо меньше, а кроме того - поля инициализируются при создании
  TOriginData_= class
    // вместо short string - лучше использовать string
    // будет меньше ошибок при случайном переполнении
    // и размер будет соответствовать хранимой строке
    SkvName,Kust,Plast:string;
    sost:boolean;
    // лучше вместо single использовать double
    // так место не сэкономить, а по быстродействию - лучше
    Bs,IsTgo,OssTgo:single;
    tgo:TDateTime;
    GRPData:GRP__;

    ZgrpInd:double;
    TP:string;
    ro,Ap,mp,Cp,muF,VF,qF:double;
    FF:byte;
    Mest,PlastGRP:string;
    TGrp:TDateTime;
end;

TOriginData=class(TComponent)
  protected
   // здесь будем хранить ВСЁ
   SkvNm        :TStringList;

   procedure    SetOriginData(const skvname:string;Value:TOriginData_);
   function     GetOriginData(const skvname:string):TOriginData_;
  public
   constructor  Create(AOwner:TComponent); override;
   destructor   Destroy; override;

   property ODWell[const skvname:string]:TOriginData_ read GetOriginData write SetOriginData; default;
end;

constructor  TOriginData.Create(AOwner:TComponent);
begin
  inherited;

  SkvNm := TStringList.Create();
  SkvNm.Sorted := true;
  // будет ругаться на дублировании имен - предупреждение для программиста об ошибках в алгоритмах
  SkvNm.Duplicates := dupError;
end;

destructor   TOriginData.Destroy;
begin
  // не забыть почистить
  while SkvNm.Count > 0 do begin
    (SkvNm.Objects[SkvNm.Count - 1]).Free();
  end;

  inherited;
end;


procedure TOriginData.SetOriginData(const skvname:string; value:TOriginData_);
var i:integer;
begin
  i:=SkvNm.IndexOf(skvname); //AnsiLowerCase - не нужно
  if i = -1 then begin
    SkvNm.AddObject(skvname, value);
  end else begin
    // не забыть грохнуть старое значение
    (SkvNm.Objects[i]).Free();
    SkvNm.Objects[i] := value;
  end;
end;

function   TOriginData.GetOriginData(const skvname:string):TOriginData_;
var i:integer;
begin
  // здесь не проверяется на ошибку
  Result:=SkvNm.Objects[SkvNm.IndexOf(skvname)];
//----------------------------
  // но можно и так
  // автосоздание записи при её отсутствии
  i:=SkvNm.IndexOf(skvname);
  if i = -1 then begin
    Result:=TOriginData_.Create();
    SkvNm.AddObject(skvname, Result);
  end else begin
    Result:=SkvNm.Objects[i];
  end;
end;
Евгений
Re[6]: Созлание property array, который работает со структур
От: nik-tmn Россия  
Дата: 27.03.03 06:40
Оценка:
Здравствуйте, mrhru,

Спасибо, почти все работает, (с указателями не получалось хранить разные значения — все записывалось по одному адресу ) но есть еще вопросы.


M>    // вместо short string - лучше использовать string
M>    // будет меньше ошибок при случайном переполнении
M>    // и размер будет соответствовать хранимой строке
Я использовал такой тип, чтобы в дальнейшем сохранять/загружать данные через TMemoryStream


M>destructor   TOriginData.Destroy;
M>begin
M>  // не забыть почистить
M>  while SkvNm.Count > 0 do begin
M>    (SkvNm.Objects[SkvNm.Count - 1]).Free();
M>  end;
В этом месте происходит AV Error, но не понятно почему...
А нельзя просто: SkvNm.Free; ?


Best regards,
Nik
Re[7]: Созлание property array, который работает со структур
От: mrhru Россия  
Дата: 27.03.03 07:15
Оценка:
Здравствуйте, nik-tmn, Вы писали:

NT>Здравствуйте, mrhru,


NT>
NT>
M>>destructor   TOriginData.Destroy;
M>>begin
M>>  // не забыть почистить
M>>  while SkvNm.Count > 0 do begin
M>>    (SkvNm.Objects[SkvNm.Count - 1]).Free();
M>>  end;
NT>


NT>В этом месте происходит AV Error, но не понятно почему...


Тьфу, ну конечно!!

  while SkvNm.Count > 0 do begin
    (SkvNm.Objects[SkvNm.Count - 1]).Free();
    SkvNm.Delete(SkvNm.Count - 1);// !!! вот это забыл
  end;


NT>А нельзя просто: SkvNm.Free; ?

И это тоже надо.

Это нужно для удаления всех объектов, находящихся в StringList, который сам этим не занимается.
    (SkvNm.Objects[SkvNm.Count - 1]).Free();
    SkvNm.Delete(SkvNm.Count - 1);



Итого, деструктор:
destructor   TOriginData.Destroy;
begin
  while SkvNm.Count > 0 do begin
    (SkvNm.Objects[SkvNm.Count - 1]).Free();
  end;

  SkvNm.Free();

  inherited;
end;
Евгений
Re[8]: Созлание property array, который работает со структур
От: nik-tmn Россия  
Дата: 27.03.03 07:23
Оценка:
Здравствуйте, mrhru, Вы писали:


NT>>
NT>>
M>>>destructor   TOriginData.Destroy;
M>>>begin
M>>>  // не забыть почистить
M>>>  while SkvNm.Count > 0 do begin
M>>>    (SkvNm.Objects[SkvNm.Count - 1]).Free();
M>>>  end;
NT>>


NT>>В этом месте происходит AV Error, но не понятно почему...


M>Тьфу, ну конечно!!


M>
M>  while SkvNm.Count > 0 do begin
M>    (SkvNm.Objects[SkvNm.Count - 1]).Free();
M>    SkvNm.Delete(SkvNm.Count - 1);// !!! вот это забыл
M>  end;
M>


С этим я уже разобрался, прошел отладчиком и увидел, что Count не уменьшается и сразу добавил такую строку


Да, как нибудь можно, чтобы при попытке чтения несуществующего параметра, выдавался нулевой результат, а не создавался параметр?

И как можно записать значения в поток?
....
var Buffer:TOriginData_
begin
buffer:=TOriginData_.create;
buffer:=OriginData['1'];
Str.Write(Buffer,SizeOf(Buffer));
....

Такое не катит , размер Buffer всегда 4 байта.

Best regards,
Nik
Re[9]: Созлание property array, который работает со структур
От: mrhru Россия  
Дата: 27.03.03 07:40
Оценка:
Здравствуйте, nik-tmn, Вы писали:

NT>Да, как нибудь можно, чтобы при попытке чтения несуществующего параметра, выдавался нулевой результат, а не создавался параметр?


А что значит "нулевой" — nil?

function   TOriginData.GetOriginData(const skvname:string):TOriginData_;
var i:integer;
begin
  i:=SkvNm.IndexOf(skvname);
  if i = -1 then begin
    Result := nil;
  end else begin
    Result := SkvNm.Objects[i];
  end;
end;



NT>И как можно записать значения в поток?

NT>
NT>....
NT>var Buffer:TOriginData_
NT>begin
NT>buffer:=TOriginData_.create;
NT>buffer:=OriginData['1'];
NT>Str.Write(Buffer,SizeOf(Buffer));
NT>....
NT>

NT>Такое не катит , размер Buffer всегда 4 байта.

... равный размеру указателя...

Надо добавить к TOriginData_ методы SaveToStream(Str: TStream) и LoadFromStream(Str: TStream), в которых
по очереди сохранять и восстанавливать поля.

PS. Я думаю, может возникнуть вопрос — а зачем всё это надо, если с record было всё легко и просто?
Если да, то ответ, наверное, — а сейчас всё прозрачно и красиво. Нет?
Евгений
Re[10]: Созлание property array, который работает со структу
От: nik-tmn Россия  
Дата: 31.03.03 05:32
Оценка:
Здравствуйте, mrhru, Вы писали:


M>А что значит "нулевой" — nil?

Нет не nil, а чтобы при к любым переменным выдавалось 0
(ODWell['1].Bs выдавало бы 0 и т.д.)


Написал вот такие обработчики:
{TOriginData_}
function TOriginData_.SaveToStream(var Str:TMemoryStream):boolean;
var Title:TTitle;
tmp:TOriginDataRecord;
begin
try
 tmp.SkvName:=SkvName; tmp.Kust:=Kust; tmp.Plast:=Plast;
 tmp.sost:=sost;
 tmp.Bs:=Bs; tmp.IsTgo:=IsTgo; tmp.OssTgo:=OssTgo; tmp.tgo:=tgo;
 tmp.GRPData:=GRPData;
 tmp.ZgrpInd:=ZgrpInd;
 tmp.TP:=TP; tmp.ro:=ro; tmp.Ap:=Ap; tmp.mp:=mp;
 tmp.Cp:=Cp; tmp.muF:=muF; tmp.VF:=VF; tmp.qF:=qF;
 tmp.FF:=FF;
 tmp.Mest:=Mest; tmp.PlastGRP:=PlastGRP;
 tmp.TGrp:=TGrp;


Title.ID:=0; Title.CountByte:=sizeof(integer);
str.Write(Title,sizeof(Title));

Title.ID:=1; Title.CountByte:=sizeof(tmp);
str.Write(Title,sizeof(Title));
str.Write(tmp,Title.CountByte);

Title.ID:=-1; Title.CountByte:=sizeof(integer);
str.Write(Title,sizeof(Title));
Result:=true;
except
 Result:=false;
end;
end;

function TOriginData_.LoadFromStream(var Str:TMemoryStream):boolean;
var Title:TTitle;
tmp:TOriginDataRecord;
begin
try
str.Read(Title,sizeof(Title));
if Title.ID<>0 then Raise Exception.Create('Error loading data');

str.Read(Title,sizeof(Title));
if Title.ID<>1 then Raise Exception.Create('Error loading data');
str.Read(tmp,Title.CountByte);

str.read(Title,sizeof(Title));
if Title.ID<>-1 then Raise Exception.Create('Error loading data');


 SkvName:=tmp.SkvName; Kust:=tmp.Kust; Plast:=tmp.Plast;
 sost:=tmp.sost;
 Bs:=tmp.Bs; IsTgo:=tmp.IsTgo; OssTgo:=tmp.OssTgo; tgo:=tmp.tgo;
 GRPData:=tmp.GRPData;
 ZgrpInd:=tmp.ZgrpInd;
 TP:=tmp.TP; ro:=tmp.ro; Ap:=tmp.Ap; mp:=tmp.mp;
 Cp:=tmp.Cp; muF:=tmp.muF; VF:=tmp.VF; qF:=tmp.qF;
 FF:=tmp.FF;
 Mest:=tmp.Mest; PlastGRP:=tmp.PlastGRP;
 TGrp:=tmp.TGrp;
Result:=true;
except
 Result:=false;
end;
end;


А в классе TOriginData:
function TOriginData.SaveToStream(var Str:TMemoryStream):boolean;
var i,count:integer;
Buffer:TOriginData_;
begin
Buffer:=TOriginData_.Create;
try
 if Str=nil then Raise Exception.Create('No stream');
 Str.Clear;
 count:=SkvNm.Count;
 //writing header
 Str.Write(Count,SizeOf(Integer));
 //writing data
 for i:=0 to Count-1 do
  (SkvNm.Objects[i] as TOriginData_).SaveToStream(str);
 //writing closing data
 Count:=-1;
 Str.Write(Count,SizeOf(Integer));
 Str.Position:=0;
 Result:=true;
Buffer.Free;
except
 Result:=false;
end;
end;

function TOriginData.LoadFromStream(Str:TMemoryStream):boolean;
var i,count:integer;
Buffer:TOriginData_;
begin
Buffer:=TOriginData_.Create;
try
 if Str=nil then Raise Exception.Create('No stream');
 Str.Position:=0;
 //getting records count
 Str.Read(Count,SizeOf(Integer));
 if count<=0 then Raise Exception.Create('No Data');
 //clearing previous
 SkvNm.Clear;
 //reading records
 for i:=0 to Count-1 do begin
  Buffer.LoadFromStream(str);
  ODWell[Buffer.SkvName]:=Buffer;
 end;
 //checking closing data
 Str.Read(Count,SizeOf(Integer));
 if Count<>-1 then Raise Exception.Create('OriginData CRC Error');
 Str.Position:=0;
 Result:=true;
except
 Result:=false;
end;
end;

Сохраняет отлично, а при загрузке возникает ошибка AV(даже не при загрузке, а при выходе из программы. Ошибка возникает в процедуре LoadFromStream в месте:
 for i:=0 to Count-1 do begin
  Buffer.LoadFromStream(str);
  ODWell[Buffer.SkvName]:=Buffer;
 end;

Как я думаю, происходит не копирование, а просто присвоение адреса? Как это можно пофиксить?

M>Если да, то ответ, наверное, — а сейчас всё прозрачно и красиво. Нет?

С этим полностью согласен

Best regards,
Nik
Re[11]: Созлание property array, который работает со структу
От: mrhru Россия  
Дата: 31.03.03 05:51
Оценка:
Здравствуйте, nik-tmn, Вы писали:

...

NT>Сохраняет отлично, а при загрузке возникает ошибка AV(даже не при загрузке, а при выходе из программы. Ошибка возникает в процедуре LoadFromStream в месте:

NT>
NT> for i:=0 to Count-1 do begin
NT>  Buffer.LoadFromStream(str);
NT>  ODWell[Buffer.SkvName]:=Buffer;
NT> end;
NT>

NT>Как я думаю, происходит не копирование, а просто присвоение адреса? Как это можно пофиксить?

Да, присвоение ссылки на один и тот же экземпляр Buffer. Возможно два выхода:

1) Создавать каждый раз новый Buffer:

 for i:=0 to Count-1 do begin
  // вот здесь 
  Buffer := TOriginData_.Create();

  Buffer.LoadFromStream(str);
  ODWell[Buffer.SkvName]:=Buffer;
 end;


но, с учетом "красоты" и избавления от возможных ошибок (можно забыть создавать новый экземпляр), имхо, лучше

2) В методе SetOriginData, производить копирование полей входного value в поля, внутри создаваемого, объекта:

procedure TOriginData.SetOriginData(const skvname:string; value:TOriginData_);
var temp : TOriginData_;
begin
  // получаем/генерируем экземпляр TOriginData_
  temp := GetOriginData(skvname);

  // и просто копируем поля из value в temp
  // по заведенной традиции, прячем этот процесс
  // в метод TOriginData_.Assign
  temp.Assign(value);

  // или так 
  //GetOriginData(skvname).Assign(value); 

end;
Евгений
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.