C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
Подкинте идею, Функцию возвращает разный тип данных?
dvv
Отправлено: 03.05.2005, 07:46


Не зарегистрирован







Можно ли написать такую функцию, которая возвращала бы нужный тип данных? Например, как нибудь так:
template <class Type> Type MyFunc(int Chooze)
{
switch
{
case 1: return (int)1;
case 2: return (float)1.1;
}
return "Параметр функции может быть только 1 или 2";
}

Т.е. в зависимости от параметра функция возвращает различный тип. [B]
Rius
Отправлено: 03.05.2005, 08:59


Мастер участка

Группа: Участник
Сообщений: 321



CODE
Variant MyFunc(int Chooze)
{
Variant res;
switch(Chooze)
{
 case 1: res = (int)1; break;
 case 2: res = (float)1.1; break;
 default: res = AnsiString("Параметр функции может быть только 1 или 2");
}
return res;
}
А зачем?
dvv
Отправлено: 03.05.2005, 09:38


Не зарегистрирован







Идея принимается!

Работая с базами данных я часто сталкивался с методом FieldByName. C его помощью можно не только читать значения полей таблицы, но и присваивать значение полям. Например:
int aaa=Table1->FieldByName("Id")->AsInteger;
Table1->FieldByName("Name")->Text="Наименование чего-то там";

Можно ли написать такую функцию(метод), которая устаналивала бы значение нужного типа данных и не просто для переменной, а для свойства компонента. Т.е. работала бы так как FieldByName, но применительно к свойствам. Например:
TLabel * Label;
. . .

Variant __fastcall TForm1::MyFieldByName(TComponent* Label1, AnsiString PropertyName)
{
if(PropertyName=="Caption")
return Label1->Caption;
}

void __fastcall TForm1::Button1Click(TObject *Sender)
{
MyFieldByName(Label1, "Caption")="Надпись";
}

Я понимаю, что Label1->Caption="Надпись" никто не отменял.
Но нужно реализовать именно через функцию(метод)! Я хочу создать объект, свойствами которого, будут компоненты TLabel, TEdit ...
Для управления свойствами компонентов объекта мне и нужна такая функция
dvv
Отправлено: 03.05.2005, 16:02


Не зарегистрирован







Упростим задачу. Есть функция
int* __fastcall Test(int* i)
{ return i;}

После выполнения кода:
int aa=10;
*(Test(&aa))=20;
aa примет значение 20. Т.к.функция вернет указатель на переменную aa. Затем значению указателя присвоим 20.

Делаем то же самое с компонентом:
int* __fastcall TForm1::Test()
{ return &(Edit1->Width);}

Выполнение кода
*(Test())=300;
должно привести к изменению ширины компонента:

На самом деле, хотя Edit1->Width изменяется, ширина компонента остается прежней. Почему?

Rius
Отправлено: 03.05.2005, 20:06


Мастер участка

Группа: Участник
Сообщений: 321



В ООП и С++ принято предотвращать прмяой доступ к членам классов. Доступ осуществляется через функции-члены, которые могут проверить правильность указываемых данных. Поэтому это &(Edit1->Width) не работает.

CODE
Variant __fastcall TForm1::MyFieldByName(TComponent* Label1, AnsiString PropertyName)
{
if(PropertyName=="Caption")
return Label1->Caption;
}
 — imho изобретение усложненной версии велосипеда, есть широко распространенный обработчики событий с параметром Sender. Например и у формы и у кнопки может быть один обработчик события OnClick:
CODE
void __fastcall TForm1::OnClick(TObject *Sender)
{
AnsiString cname = Sender->ClassName();
if(cname == "TForm") Caption = "Form clicked";
if(cname == "TButton") Caption = "Button clicked";
}
dvv
Отправлено: 03.05.2005, 21:25


Не зарегистрирован







C первой частью "марлизонского балета" разобрались. Теперь мы знаем как написать фунуцию, которая возвращеает любой тип. В моей реализации она будет иметь вид:
Variant GetProretyValue(TComponent* Component, AnsiString PropertyName);
Функция получает адрес компонета и имя свойства, а возвращает значение свойства.

Вторая часть "марлизонского балета" заключается в том, что бы уже не читать значения свойств, а присваивать им значения. Например:
SetProretyValue(TComponent* Component, AnsiString PropertyName,Variant Value);
Как написать такую функцию (SetProretyValue) мне понятно. В принцыпе достаточно того, что будут делать эти две функции.

Третья часть "марлизонского балета" — хочется заменить две функции одной. Ведь мы стремимся к краткости кода! Сделать, например так, как работает FieldByName. Если, конечно, это возможно. А как такое сделать я не знаю. Может кто подскажет?
Rius
Отправлено: 03.05.2005, 22:01


Мастер участка

Группа: Участник
Сообщений: 321



Например создать класс, который имеет индексирумое строкой свойство с функциями доступа, а значение есть Variant.
dvv
Отправлено: 04.05.2005, 06:48


Не зарегистрирован







Что имеется ввиду "индексирумое строкой свойство"?
Rius
Отправлено: 04.05.2005, 09:30


Мастер участка

Группа: Участник
Сообщений: 321



QUOTE
Свойство-массив
Некоторые свойства могут быть массивами, а не только такими простыми типами данных как bool, int и AnsiString. Этот аспект не очень хорошо документирован для пользователей. Примером свойства-массива является свойство Lines компонента ТМеmо. Это свойство позволяет пользователю осуществлять доступ к отдельным строкам компонента Memo.
Свойство-массив объявляется точно так же, как и другие свойства, но имеет два отличия: объявление включает соответствующие индексы с заданными типами, и индексы не обязательно целые числа. В листингах 9.12-9.15 приведен пример использования двух свойств. Одно содержит в качестве индекса строку, а второе -- целое число.

Листинг 9.12. Использование строки в качестве индекса
CODE
class PACKAGE TStringAliasComponent : public TComponent
{private:
TStringList RealList; TStringList AliasList;
AnsiString _fastcall GetStringAlias(AnsiString RawString);
AnsiString _fastcall GetRealString(int Index);
void fastcall SetRealString(int Index, AnsiString Value);
public:
property AnsiString AliasString[AnsiString RawString] =
{read = GetStringAlias};
property AnsiString RealString[int Index] =
{read=GetRealString, write=SetRealString};
}
В этом примере компонент может хранить список строк вместе со списком псевдонимов. Свойство AliasString принимает значение RawString и возвращает псевдоним с помощью метода GetStringAlias(). При создании свойств-компонентов многие разработчики смущены тем, что в объявлении используется индексная нотация (т.е. квадратные скобки []), а в коде используется такой же способ записи, как и при вызове обычного метода. Например, свойство Real String имеет не только возвращаемое значение типа AnsiString, но и целое число в качестве индекса. Метод GetRealString() используется для извлечения строки списка на основе заданного значения индекса, как показано в листинге 9.13.

Листинг 9.13. Метод чтения свойства-массива
CODE
AnsiString _fastcall TStringAliasComponent::GetRealString(int Index)
{
if(Index > (RealList->Count — 1))
return ""; return RealList->Strings[Index];
}

В коде это свойство будет выглядеть следующим образом. AnsiString str = StringAlias1->RealString[0];
Рассмотрим подробнее метод SetRealString(). На первый взгляд объявление этого метода выглядит несколько странно. Первый параметр является целочисленной переменной и используется как индекс, а второй — - переменной типа AnsiString. Переменная RealList типа TStringList вставляет в список строку типа AnsiString в месте, указанном с помощью целочисленного параметра-индекса. В листинге 9.14 показан код определения метода SetRealString().

Листинг 9.14. Метод записи значений свойства-массива
CODE
void fastcall TStringAliasComponent::SetRealString(int Index, AnsiString Value)
{
if((RealList->Count — 1)) < Index)
RealList->Add(Value); else
RealList->Insert(Index, Value);
}

В листинге 9.14 значение параметра Index сверяется с количеством строк, которые уже находятся в списке. Если значение параметра Index больше этого количества, то строка Value просто добавляется в конец списка. В противном случае вызывается метод Insert () объекта TStringList для вставки строки в месте, указанном значением индекса Index. Теперь в этот список можно вставить следующую строку.
StringAliasl->RealString[l] == "некоторая строка";
Рассмотрим еще один интересный момент. Метод GetStringAlias() используется для чтения значения свойства AliasString, в котором в качестве индекса используется строка. Список строк является массивом строк, поэтому каждая строка имеет индекс или определенное место в этом списке. Метод IndexOf () компонента TStringList использу¬ется для сравнения переданной строки со строками, которые уже находятся в списке. Этот метод возвращает целое значение, которое является индексом строки в этом спи¬ске, или значение -1, если такой строки нет. Теперь остается только вернуть из списка псевдонимов строку с индексом, указанным в вызове метода IndexOf (). Соответствую¬щий код показан в листинге 9.15.

Листинг 9.15. Метод GetStringAlias()
CODE
AnsiString _fastcall TStringAliasComponent::GetStringAlias(
AnsiString RawString)
{
int Index;
Index = RealList->IndexOf(RawString); if((Index == -1) (Index >(AliasList->Count-l))) return RawString;
return AliasList->Strings [Index ];
_}

Для применения этого свойства можно использовать следующий код.
CODE
AnsiString MyAliasString =
StringAliasl->AliasString("The Raw String");


Источник: http://progz.ru/forum/viewtopic.php?t=11128
Jarrod Hollingworth, Dan Butterfield, Bob Swart, Jamie Allsop
C++Builder 5 Developer 's Guide,
Глава 9 "Создание пользовательских компонентов".

Впрочем FiledByname можно считать возвращает указатель на объект Field, так что делаешь один класс для доступа к свойству (={read=..., write=...}), и вкладываешь его в другой класс, который обращается к компонентам.

Отредактировано Rius — 04/05/2005, 12:37
dvv
Отправлено: 05.05.2005, 08:56


Не зарегистрирован







Вот это в самое яблочко. Как раз такое мне и нужно! Спасибо.В результате удалось реализовать код:

TRo* Ro1 = new TRo(toLabel); // Создает объект по виду как TLabel
TRo* Ro2 = new TRo(toEdit); // Создает объект по виду как TEdit
Ro1->PropByName["Caption"] = "Значение";
Ro2->PropByName["Text"] = "100";
ShowMessage(Ro1->PropByName["Caption"]+" = "+Ro2->PropByName["Text"]); // Краткость — сестра таланта (и теща гонорара)

Не ясно только как работает компилятор. В принципе, это уже другая тема. Но все же. Например мы объявляем метод:

void __fastcall SetRealString(int Index, AnsiString Value);

А затем вызываем его :

StringAliasl->RealString[l] = "некоторая строка";

Не совсем понятно почему переменной l ставиться в соответствие int Index, а "некоторая строка" соответствует AnsiString Value?
Ну ладно здесь типы int и AnsiString разные. Вроде как можно разобраться. А если бы мы присваивали значение int вместо строки? А если бы в метод SetRealString объявить не два и три параметра? Есть ли по этому поводу какие либо правила?
Rius
Отправлено: 05.05.2005, 20:10


Мастер участка

Группа: Участник
Сообщений: 321



QUOTE
Не совсем понятно почему переменной l ставиться в соответствие int Index, а "некоторая строка" соответствует AnsiString Value?
Ну ладно здесь типы int и AnsiString разные. Вроде как можно разобраться. А если бы мы присваивали значение int вместо строки? А если бы в метод SetRealString объявить не два и три параметра? Есть ли по этому поводу какие либо правила?
это в книге описано. разберешь несколько примеров и заметишь закономерности. я сам пока только с одномерными массивами работал, да по двумерным советовал.

Вернуться в Вопросы программирования в C++Builder