процедурные переменные

Форум для изучающих FPC и их учителей.

Модератор: Модераторы

процедурные переменные

Сообщение AlexP » 20.01.2010 14:50:58

У меня есть обьект, который работает с процедурной переменной.

Только иногда приходится работать не только с обычными функциями, а еще с функциями, которые являются методом какого-нибудь обьекта.

Но такая функция, хоть и имеет такой же список параметров, является другим типом процедурной переменной, т.е. получается, что для того, чтобы работать ещё и с такими функциями, мне надо написать еще один такой же точно обьект, только для работы с процедурной переменной этого типа. Т.е. абсолютно одинаковый код увеличивается в два раза. Это можно как-нибудь обойти?
AlexP
новенький
 
Сообщения: 20
Зарегистрирован: 11.05.2007 19:04:01

Re: процедурные переменные

Сообщение AbakAngelSoft » 20.01.2010 14:54:43

Даже если метод имеет тот-же список параметров - у него есть первый скрытый параметер Self. И ссылка на метод хранит помимо адреса процедуры адрес объекта к которому относится процедура для того что-бы при вызове восстановить этот параметер!

Добавлено спустя 4 минуты 49 секунд:
как вариант можно в процедурные типы добавить фиктивный параметер типа pointer при сохранении приводить процедурную переменную к ссылке на метод.
Met: TMethod;
Met.Code := Pointer(<процедурная переменная>)
Met.Data := nil;
А вызывать так как вызывается метод в процедуре игнорируя первый параметер.
Аватара пользователя
AbakAngelSoft
постоялец
 
Сообщения: 273
Зарегистрирован: 06.08.2008 19:28:26
Откуда: Краснодар

Re: процедурные переменные

Сообщение Astralis » 20.01.2010 17:03:20

насчет вызова немного уточню: такие методы не должны использовать в своем теле Self
если же они в своем теле используют Self, то его явно нужно передавать
в арихитектуре Intel при соглашении вызова thiscall можно явно передать параметр self
Код: Выделить всё
asm
   MOV ECX, NeedSelf
   end;
ReturnValue:=ExecuteFunction(<params list>);


class-методы полностью совместими с обычными функциями
Аватара пользователя
Astralis
новенький
 
Сообщения: 45
Зарегистрирован: 06.06.2007 20:33:05
Откуда: Tvercity-Annet

Re: процедурные переменные

Сообщение Mr.Smart » 20.01.2010 22:28:37

Astralis
А вот и нет! Все функции класса имеют скрытый первый параметр, который и является указателем на self :wink:
Mr.Smart
долгожитель
 
Сообщения: 1796
Зарегистрирован: 29.03.2008 01:01:11
Откуда: из леса!

Re: процедурные переменные

Сообщение Sergei I. Gorelkin » 20.01.2010 23:07:51

Эта цитата из документации C/C++ вообще не имеет никакого отношения к FPC.
Начиная с того, что тип вызова thiscall отсутствует, первый параметр передается не в ecx, а в eax (при типе вызова register, иначе в стеке), ассемблерная вставка перед вызовом ф-ции никакого эффекта иметь не будет, т.к. вызов разместит все параметры на нужных местах.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1397
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: процедурные переменные

Сообщение Astralis » 21.01.2010 01:17:07

Приятно удивлен, что в FPC с классами все гораздо и внятнее
в этом плане вызов метода как функции легок и прост
Код: Выделить всё
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.
Аватара пользователя
Astralis
новенький
 
Сообщения: 45
Зарегистрирован: 06.06.2007 20:33:05
Откуда: Tvercity-Annet

Re: процедурные переменные

Сообщение Sergei I. Gorelkin » 21.01.2010 02:08:44

Когда вызываемая функция определена как stdcall или cdecl, то все параметры передаются в стеке (включая скрытый self) и нет ну просто никакой необходимости что-то писать в регистры с помощью ассемблерных вставок. Да, этот пример работоспособен, потому что регистр ecx в таком месте можно перезаписать чаще всего без последствий. Но зачем?
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1397
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: процедурные переменные

Сообщение AbakAngelSoft » 21.01.2010 10:54:48

Я и говорил что 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>);

Не взирая на то - метод там или процедура
Аватара пользователя
AbakAngelSoft
постоялец
 
Сообщения: 273
Зарегистрирован: 06.08.2008 19:28:26
Откуда: Краснодар

Re: процедурные переменные

Сообщение Astralis » 22.02.2010 20:18:47

thiscall в FPC в явном виде не используется, но он неявно возникает при импорте классов, написанных на c++, например при работе с OpenOffice. Импорт отдельных методов из dll-файла есть более гибкое решение, чем использование ключевого слова cppclass.

Наример, есть класс написанный на 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
Аватара пользователя
Astralis
новенький
 
Сообщения: 45
Зарегистрирован: 06.06.2007 20:33:05
Откуда: Tvercity-Annet

Re: процедурные переменные

Сообщение Sergei I. Gorelkin » 22.02.2010 20:59:11

Эта конструкция рассыплется к чертовой бабушке при наличии у функций аргументов.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1397
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: процедурные переменные

Сообщение sign » 13.04.2010 14:52:02

У меня по теме вопрос.
Пытаюсь прикрутить библиотеку 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"/
Как будто Лазарус определяет, что тут идет вызов фукции, а не присвоение.
Что тут не так и как можно исправить?
sign
энтузиаст
 
Сообщения: 1131
Зарегистрирован: 30.08.2009 09:20:53

Re: процедурные переменные

Сообщение Mr.Smart » 13.04.2010 15:00:39

Код: Выделить всё
comparator := @DObjectComparator;
Mr.Smart
долгожитель
 
Сообщения: 1796
Зарегистрирован: 29.03.2008 01:01:11
Откуда: из леса!

Re: процедурные переменные

Сообщение sign » 13.04.2010 15:29:47

Спасибо.
sign
энтузиаст
 
Сообщения: 1131
Зарегистрирован: 30.08.2009 09:20:53


Вернуться в Обучение Free Pascal

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 21

Рейтинг@Mail.ru