процедурные переменные
Модератор: Модераторы
процедурные переменные
У меня есть обьект, который работает с процедурной переменной.
Только иногда приходится работать не только с обычными функциями, а еще с функциями, которые являются методом какого-нибудь обьекта.
Но такая функция, хоть и имеет такой же список параметров, является другим типом процедурной переменной, т.е. получается, что для того, чтобы работать ещё и с такими функциями, мне надо написать еще один такой же точно обьект, только для работы с процедурной переменной этого типа. Т.е. абсолютно одинаковый код увеличивается в два раза. Это можно как-нибудь обойти?
Только иногда приходится работать не только с обычными функциями, а еще с функциями, которые являются методом какого-нибудь обьекта.
Но такая функция, хоть и имеет такой же список параметров, является другим типом процедурной переменной, т.е. получается, что для того, чтобы работать ещё и с такими функциями, мне надо написать еще один такой же точно обьект, только для работы с процедурной переменной этого типа. Т.е. абсолютно одинаковый код увеличивается в два раза. Это можно как-нибудь обойти?
- AbakAngelSoft
- постоялец
- Сообщения: 273
- Зарегистрирован: 06.08.2008 19:28:26
- Откуда: Краснодар
- Контактная информация:
Даже если метод имеет тот-же список параметров - у него есть первый скрытый параметер Self. И ссылка на метод хранит помимо адреса процедуры адрес объекта к которому относится процедура для того что-бы при вызове восстановить этот параметер!
Добавлено спустя 4 минуты 49 секунд:
как вариант можно в процедурные типы добавить фиктивный параметер типа pointer при сохранении приводить процедурную переменную к ссылке на метод.
Met: TMethod;
Met.Code := Pointer(<процедурная переменная>)
Met.Data := nil;
А вызывать так как вызывается метод в процедуре игнорируя первый параметер.
Добавлено спустя 4 минуты 49 секунд:
как вариант можно в процедурные типы добавить фиктивный параметер типа pointer при сохранении приводить процедурную переменную к ссылке на метод.
Met: TMethod;
Met.Code := Pointer(<процедурная переменная>)
Met.Data := nil;
А вызывать так как вызывается метод в процедуре игнорируя первый параметер.
- Astralis
- новенький
- Сообщения: 45
- Зарегистрирован: 06.06.2007 20:33:05
- Откуда: Tvercity-Annet
- Контактная информация:
насчет вызова немного уточню: такие методы не должны использовать в своем теле Self
если же они в своем теле используют Self, то его явно нужно передавать
в арихитектуре Intel при соглашении вызова thiscall можно явно передать параметр self
class-методы полностью совместими с обычными функциями
если же они в своем теле используют Self, то его явно нужно передавать
в арихитектуре Intel при соглашении вызова thiscall можно явно передать параметр self
Код: Выделить всё
asm
MOV ECX, NeedSelf
end;
ReturnValue:=ExecuteFunction(<params list>);
class-методы полностью совместими с обычными функциями
Astralis
А вот и нет! Все функции класса имеют скрытый первый параметр, который и является указателем на self
А вот и нет! Все функции класса имеют скрытый первый параметр, который и является указателем на self
- Sergei I. Gorelkin
- энтузиаст
- Сообщения: 1409
- Зарегистрирован: 24.07.2005 14:40:41
- Откуда: Зеленоград
Эта цитата из документации C/C++ вообще не имеет никакого отношения к FPC.
Начиная с того, что тип вызова thiscall отсутствует, первый параметр передается не в ecx, а в eax (при типе вызова register, иначе в стеке), ассемблерная вставка перед вызовом ф-ции никакого эффекта иметь не будет, т.к. вызов разместит все параметры на нужных местах.
Начиная с того, что тип вызова thiscall отсутствует, первый параметр передается не в ecx, а в eax (при типе вызова register, иначе в стеке), ассемблерная вставка перед вызовом ф-ции никакого эффекта иметь не будет, т.к. вызов разместит все параметры на нужных местах.
- Astralis
- новенький
- Сообщения: 45
- Зарегистрирован: 06.06.2007 20:33:05
- Откуда: Tvercity-Annet
- Контактная информация:
Приятно удивлен, что в FPC с классами все гораздо и внятнее
в этом плане вызов метода как функции легок и прост
что касается соглашения thiscall - то его нет в FPC, но оно может неявно возникнуть, когда происходит импорт классов из dll, созданных в C++ (например, UNO). в этом случае код привенный в предыдущем сообщении, вполне работоспособен (при условии, что функция определена как stdcall), разве что в C++ конструктор не выделяет память под объект, а класс-методы используют соглашение cdecl.
в этом плане вызов метода как функции легок и прост
Код: Выделить всё
type
TA = procedure(<param list>) of object;
TB = procedure(Self: TObject; <param list>);
var A: TA; B: TB;
...
B:=TB(TMethod(A).Code);
B(TMethod(A).Data,<param list>);
что касается соглашения thiscall - то его нет в FPC, но оно может неявно возникнуть, когда происходит импорт классов из dll, созданных в C++ (например, UNO). в этом случае код привенный в предыдущем сообщении, вполне работоспособен (при условии, что функция определена как stdcall), разве что в C++ конструктор не выделяет память под объект, а класс-методы используют соглашение cdecl.
- Sergei I. Gorelkin
- энтузиаст
- Сообщения: 1409
- Зарегистрирован: 24.07.2005 14:40:41
- Откуда: Зеленоград
Когда вызываемая функция определена как stdcall или cdecl, то все параметры передаются в стеке (включая скрытый self) и нет ну просто никакой необходимости что-то писать в регистры с помощью ассемблерных вставок. Да, этот пример работоспособен, потому что регистр ecx в таком месте можно перезаписать чаще всего без последствий. Но зачем?
- AbakAngelSoft
- постоялец
- Сообщения: 273
- Зарегистрирован: 06.08.2008 19:28:26
- Откуда: Краснодар
- Контактная информация:
Я и говорил что Self в процедуре просто не использовать.
Только удобнее вызывать все как методы:
И вызываем
Не взирая на то - метод там или процедура
Только удобнее вызывать все как методы:
Код: Выделить всё
type
TA = procedure(<param list>) of object;
TB = procedure(Self: TObject; <param list>);
TCaller = class(...)
private
FMeth: TA;
public
property Meth: TA read FMeth write FMeth;
property Proc: TB read GetProc write SetProc;
end;
function TCaller.GetProc: TB;
begin
Result := TB(TMethod(FMeth).Code);
end;
procedure TCaller.SetProc(AValue: TB);
begin
TMethod(FMeth).Code := Pointer(AValue);
TMethod(FMeth).Data := nil;
end;И вызываем
Код: Выделить всё
FMeth(<param list>);Не взирая на то - метод там или процедура
- Astralis
- новенький
- Сообщения: 45
- Зарегистрирован: 06.06.2007 20:33:05
- Откуда: Tvercity-Annet
- Контактная информация:
thiscall в FPC в явном виде не используется, но он неявно возникает при импорте классов, написанных на c++, например при работе с OpenOffice. Импорт отдельных методов из dll-файла есть более гибкое решение, чем использование ключевого слова cppclass.
Наример, есть класс написанный на c++
С помощью утилиты dumpbin легко получить таблицу экспортируемых методов.
Соответственно эти функции импортируются в программе на fpc:
c++ при вызове методов использует thiscall, соответственно это тот же stdcall но дополнительно в регистре ECX передается узаказтель на объект (this). Соответственно выполнение программы на fpc выглядит следующим образом:
результат:
Наример, есть класс написанный на c++
Код: Выделить всё
class __declspec(dllexport) Foo
{
public:
Foo()
{
i = 0;
cout<<"create"<<endl;
}
~Foo()
{
cout<<"death"<<endl;
}
void inc()
{
i++;
cout<<"increment"<<endl;
}
void print()
{
cout<<"value is "<<i<<endl;
}
private:
int i;
};
С помощью утилиты dumpbin легко получить таблицу экспортируемых методов.
Код: Выделить всё
0 00001AF0 ??0Foo@@QAE@XZ public: __thiscall Foo::Foo(void)
1 00001B20 ??1Foo@@QAE@XZ public: __thiscall Foo::~Foo(void)
2 000013D0 ??4Foo@@QAEAAV0@ABV0@@Z public: class Foo & __thiscall Foo::operator=(class Foo const &)
3 00001B60 ?inc@Foo@@QAEXXZ public: void __thiscall Foo::inc(void)
4 00001BA0 ?print@Foo@@QAEXXZ public: void __thiscall Foo::print(void)
Соответственно эти функции импортируются в программе на fpc:
Код: Выделить всё
procedure FooCreate;stdcall;external 'wins.dll' name '??0Foo@@QAE@XZ';
procedure FooDestroy;stdcall;external 'wins.dll' name '??1Foo@@QAE@XZ';
procedure FooInc;stdcall;external 'wins.dll' name '?inc@Foo@@QAEXXZ';
procedure FooPrint;stdcall;external 'wins.dll' name '?print@Foo@@QAEXXZ';
c++ при вызове методов использует thiscall, соответственно это тот же stdcall но дополнительно в регистре ECX передается узаказтель на объект (this). Соответственно выполнение программы на fpc выглядит следующим образом:
Код: Выделить всё
GetMem(this,size);
asm
MOV ECX, this
end;
FooCreate;
asm
MOV ECX, this
end;
FooInc;
asm
MOV ECX, this
end;
FooInc;
asm
MOV ECX, this
end;
FooPrint;
asm
MOV ECX, this
end;
FooDestroy;
FreeMem(this);
результат:
Код: Выделить всё
create
increment
increment
value is 2
death
- Sergei I. Gorelkin
- энтузиаст
- Сообщения: 1409
- Зарегистрирован: 24.07.2005 14:40:41
- Откуда: Зеленоград
Эта конструкция рассыплется к чертовой бабушке при наличии у функций аргументов.
У меня по теме вопрос.
Пытаюсь прикрутить библиотеку DeCAL и встретил такое.
И вот на последнем присваивании Лазарь ругается: "Error: Wrong number of parameters specified for call to "DObjectComparator"/
Как будто Лазарус определяет, что тут идет вызов фукции, а не присвоение.
Что тут не так и как можно исправить?
Пытаюсь прикрутить библиотеку DeCAL и встретил такое.
Код: Выделить всё
...
DComparator = function (const obj1, obj2 : DObject) : Integer of object;
...
protected
comparator : DComparator;
...
function DObjectComparator(const obj1, obj2 : DObject) : Integer;
...
constructor DContainer.create;
begin
comparator := DObjectComparator;
end;
И вот на последнем присваивании Лазарь ругается: "Error: Wrong number of parameters specified for call to "DObjectComparator"/
Как будто Лазарус определяет, что тут идет вызов фукции, а не присвоение.
Что тут не так и как можно исправить?
Код: Выделить всё
comparator := @DObjectComparator;Спасибо.
