Форум: "Основная";
Текущий архив: 2002.10.31;
Скачать: [xml.tar.bz2];
Вниз
Неявное описание типа данных? (Структура для настроек программы) Найти похожие ветки
← →
lipskiy (2002-10-20 23:17) [0]Требуется создать класс, каждый экземпляр которого будет работать только с одним типом данных, но в описании класса (его свойств и методов) тип явно не указывать. То есть конкретный тип данных будет задан при создании экземпляра класса, и до убиения меняться не будет. Все методы и свойства класса таковы, что их работа не зависит от типа данных, но при вызове свойств и методов класса необходимо узнать у него его тип данных.
Предполагается использовать типы: string, integer, real, boolean.
Возможно ли описать такой класс? И как?
Конкретно, что мне нужно, это создать класс для настроек программы, с которым было бы удобно работать. То есть этот класс по сути будет заниматься хранением данных, записью и чтением инишки, вызовом обработчиков изменения настроек, применением настроек по умолчанию и т.п. Также нужно как-то связать каждый параметр с визуальным компонентом на динамической форме настроек.
А может быть есть принципиально иной подход?
← →
Оливейра (2002-10-21 01:14) [1]А никак. В C++ это делается шаблонами, делфи пока до этого не дорос(ла, ло). Т.е., можно либо создать один класс-предок и от него 4 "перегруженных" потомка, либо извращаться на основе более-менее универсальных встроенных контейнеров типа TList и пр., либо юзать варианты. Одним словом, Delphi для этой задачи не очень удобный инструмент.
Аргумент - родной класс TRegistry для аналогичных целей использует ряд соответствующих функций - readInteger, readString и т.п.
← →
lipskiy (2002-10-21 01:22) [2]Ясно, я так и думал...
Придется писать четыре класса, видимо это проще и правильнее будет в данном случае.
← →
PVOzerski (2002-10-21 03:41) [3]Ну, вот что... Касательно шаблонов: во-первых, это игры с С++-ным препроцессором (развитие макроподстановок), IMHO, к этапу Run-time отношения не имеюшие (здесь, впрочем, я могу и ошибаться), во-вторых, с некоторыми извратами и вопреки воле разработчиков Object Pascal, они реализуемы в Delphi игрой с include-файлами (подробнее - ищи материал на Королевстве), в четвертых - с properties номер не пройдет, но с нетипизированными аргументами в некоторых случаях кое-что выйдет
(array of const, например, решит проблему распознания типа, а дальше - забота программиста) - не говоря уже о variant. IMHO, сила шаблонов - в их сочетаемости с перегружаемыми (в том числе и в RTL) операторами и функциями, а в остальных случаях без них обойтись вполне можно.
← →
Rouse_ (2002-10-21 03:55) [4]А вот у меня есть еще предложение, (сильно ногами не бейте только) создать процедуру которая будет сама конвертировать входные данные в формат с которым работает класс (если это конечно возможно по условию)
Желаю успехов
← →
Александр С. (2002-10-21 07:51) [5]Господа!
А указатели и overload функции/процедуры разве не позволят сделать то, что требуется.
← →
Anatoly Podgorestky (2002-10-21 07:59) [6]Может Variant
← →
Opuhshii (2002-10-21 08:51) [7]2Александр С. © (21.10.02 07:51)
"Требуется создать класс, каждый экземпляр которого будет работать..." не 4 и не 5 классов.... ;)
Variant... либо указатели и память выделять от типа данных,..
← →
NailS (2002-10-21 09:21) [8]
> lipskiy © (20.10.02 23:17)
> создать класс для настроек программы, с которым было бы
> удобно работать
Как вариант, для хранения настроек можно создать класс, который умеет сохранять параметры куда-то и читать параметры откуда-то. В качестве самих параметров использовать published properties класса. Их список и тип данных можно получить динамически (GetPropList).
Таким образом в дальнейшем создание класса, содержащего необходимые тебе параметры будет выглядеть как наследование от базового класса, добавление необходимых свойств и указание пути откуда читать параметры. Все.
← →
REA (2002-10-21 11:03) [9]Можно Variant - в нем и тип есть. Придется правда проверки на тип делать всегда.
← →
Ydna (2002-10-21 11:52) [10]type
TMyTypes=(mtString,mtBoolean, mtInteger, mtReal);
TDataRecord=record case AType:TMyTypes of
mtString: (strValue :PString);
mtBoolean:(boolValue:boolean);
mtInteger:(intValue :integer);
mtReal: (realValue:real);
end;
TMyClass=class
private
data:TDataRecord;
//...
end;
В TDataRecord.AType - тип, прочие значения лежат по одному адресу.
Единственное, строки придется хранить как указатели.
← →
алгоритм Бойера - Мура, (2002-10-21 13:03) [11]Я когда-то писал нечто подобное.
В упрощённом виде это выглядело примерно так:
TGfgItem = class
private
FName: string;
FData: Pointer;
FTypeID: TTypeID;
// FIsProp: Boolean;
protected
function GetValue: string; virtual;
procedure SetValue(const AValue: string); virtual;
public
property Value: string read GetValue write SetValue;
property Name: string read FName;
constructor Create(const AName: string; var AData: Integer); overload;
constructor Create(const AName: string; var AData: string); overload;
constructor Create(const AName: string; var AData: Boolean); overload;
constructor Create(const AName: string; var AData: Double); overload;
// constructor CreateProp(const AName, APropName: string; AInstance: TObject);
end;
constructor TGfgItem.Create(const AName: string; var AData: string);
begin
FName := AName;
FData := @AData;
FTypeID := tpString;
end;
constructor TGfgItem.Create(const AName: string; var AData: Integer);
begin
FName := AName;
FData := @AData;
FTypeID := tpInteger;
end;
constructor TGfgItem.Create(const AName: string; var AData: Double);
begin
FName := AName;
FData := @AData;
FTypeID := tpReal;
end;
constructor TGfgItem.Create(const AName: string; var AData: Boolean);
begin
FName := AName;
FData := @AData;
FTypeID := tpBoolean;
end;
function TGfgItem.GetValue: string;
begin
case FTypeID of
tpInteger: Result := IntToStr(Integer(FData^));
tpString: Result := AnsiString(FData^);
tpBoolean: if Boolean(FData^) then Result := "True" else Result := "False"; //On/Off
tpReal: Result := Format("%5.3g", [Double(FData^)]);
end;
end;
procedure TGfgItem.SetValue(const AValue: string);
begin
case FTypeID of
tpInteger: Integer(FData^) := StrToInt(AValue);
tpString: AnsiString(FData^) := AValue;
tpBoolean: Boolean(FData^) := CompareText(AValue, "True") = 1;
tpReal: Double(FData^) := StrToFloat(AValue);
end;
end;
← →
reonid (2002-10-21 13:04) [12]Прошу прощения, ник не поменял.
← →
REA (2002-10-21 13:11) [13]Ну в принципе Variant и получился, только хуже
← →
reonid (2002-10-21 13:41) [14]2REA ©
Тут несколько иной принцип. Это не вариант как значение,
а нечто вроде вариантной ссылки со строковым интерфейсом.
Я их использовал примерно так:
CgfFile.Add(TCfgItem.CreateProp("MainFormWidth", "Width", MainForm));
CgfFile.Add(TCfgItem.Create("Count", cnt));
...
CfgFile.Load;
// В Form1.Width и переменную cnt загрузились значения из файла
//
CfgFile.Store;
// В файл сохранились значения MainForm.Width и cnt
//
Или:
function TDlgForm.Execute: Boolean;
begin
Edit1.Text := item.Value;
Result := ShowModal = mrOk;
if Result then item.Value := Edit1.Text;
end;
← →
Ydna (2002-10-21 13:45) [15]Угу. A как быть в случае:
var
Item:TGfgItem;
procedure foo;
var
s:string;
begin
s:="BUG!";
Item:=TGfgItem.Create(s,s);
end;
begin
foo;
ShowMessage(Item.Value); // И тут кылдык, поскольку FData ссылается в никуда
end;
← →
reonid (2002-10-21 13:46) [16]2REA ©
И, если уж на то пошло, это было написано на трубопаскале под ДОС, когда никаких вариантов ещё не было и в помине.
← →
reonid (2002-10-21 13:53) [17]2Ydna ©
Именно так, и никак иначе.
На самом деле было ещё хуже:
var
w: Word;
procedure foo;
begin
Item:=TGfgItem.Create("I: Integer", W); // Ёёёё
end;
Ну не совсем безопасный приём программирования, соглашусь.
А кому сейчас легко?
← →
Ydna (2002-10-21 14:00) [18]2reonid ©
А копировать значение, вместо передачи указателя никак? ;-)
Для строк, например, есть NewStr и DisposeStr, хоть и не рекомендуются Борландом, но работают:
constructor TGfgItem.Create(const AName, AData: string);
begin
FName := AName;
FData :=NewStr(AData);
FTypeID := tpString;
end;
destructor Destroy;
begin
if FTypeID = tpString then DisposeStr(FData);
inherited;
end;
а для всего прочего хранить в UNION (см. 21.10.02 11:52)
← →
reonid (2002-10-21 14:28) [19]2Ydna ©
Так копировать можно и с Вариантами.
Тут сама суть в том, что TCfgItem является неким посредником для реально существующей переменной/свойства и позволяет через строковый интерфейс их (реальные переменные) читать и менять.
← →
Ydna (2002-10-21 14:39) [20]2reonid ©
Не очень понятно только какую выгоду (кроме веселой отладки и возможности работать с некоторыми типами как со строками) можно получить от такого посредника...
Я писал компоненту для хранения настроек и делал там так:
1) Все сохраняемые значения описываются классом-наследником TCollectionItem (чтобы описывать их в дизайнере).
2) Значение в памяти хранится либо как строка, либо, если элемент коллекции связан со свойством некоторого компонента - то в этом свойстве компонента и храниться (вот тут ссылка).
Хранить значения как строки не больно - в инишнике все равно все строками будет, а с памятью сейчас проблем не бывает ;-)
← →
reonid (2002-10-21 14:59) [21]2Ydna ©
Ну видишь, в случае свойств ты всё же пользуешься ссылками
(ну некоторым их подобием).
Ну а у меня хранятся ссылки не только на св-ва,
но и на переменные (или поля объектов), чтобы реально всё
информация хранилась там, где ей положено - не из-за памяти,
а чтобы избежать дублирования.
Обычно объект предоставлял программе свою конфигурационную секцию, через которую можно было сохранять и загружать состояние объекта.
← →
lipskiy (2002-10-22 00:24) [22]
> Ydna © (21.10.02 11:52)
Че-т я не очень понял, как потом обращаться к элементу записи - ведь придется явно указывать поле, а значит - и тип?
> reonid © (21.10.02 13:03)
Че-т я не очень понял, как получить параметр не в виде строки, а в виде его типа данных?
Честно говоря, оба варианта мне нравятся, так как находятся на грани моего понимания :) Но как-же все-таки лучше-то?
Ребята, если не затруднит, нельзя ли конкретный примерчик для двух типов данных - string и integer:
- описание класса для одного параметра;
- пример создания списка параметров - списка экзепляров;
- записать новое значение string или integer;
- прочитать значение string или integer;
Я просто не могу пока охватить идею целиком, пример бы очень помог (можно просто с лету написать, я разберусь).
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2002.10.31;
Скачать: [xml.tar.bz2];
Память: 0.51 MB
Время: 0.009 c