Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Delphi [игнор отключен] [закрыт для гостей] / опять об указателях на процедуру - не работает код / 12 сообщений из 12, страница 1 из 1
20.08.2003, 10:26
    #32241259
Guilty
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
опять об указателях на процедуру - не работает код
Разбираясь давеча с указателями на процедуры, мне показалось чрезвычайно заманчивым использовать их при написании собственных классов (наверняка это не ново), когда метод класса узнает что же он должен делать только во время выполнения (условно). Решив опробовать это на элементарном маленьком проекте с одним юнитом - формочка с кнопочкой и мемо, а также класс, при создании экземпляра этого класса, конструктору передаётся указатель на процедуру, которая реально и должна выполняться при вызове метода MyProc. Компилится проект прекрасно, но вот на этапе выполнения выдается ошибка: нарушение доступа по адресу... Почему это происходит?

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
unit Unit1;

interface

uses
  ...
type
  TMyMethod = procedure;

  TMyClass = class
  private
    Method: pointer;
  public
    constructor Create(p: pointer);
    procedure MyProc;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    MyObject: TMyClass;

    procedure SomeWork; 
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  MyObject := TMyClass.Create(@TForm1.SomeWork);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  MyObject.Free;
end;

procedure TForm1.SomeWork;
begin
  Memo1.Lines.Append('ttt');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  MyObject.MyProc;
end;

{ TMyClass }

constructor TMyClass.Create(p: pointer);
begin
  Method := p;
end;

procedure TMyClass.MyProc;
begin
  TMyMethod(Method);
end;

end.
...
Рейтинг: 0 / 0
20.08.2003, 10:41
    #32241310
DmitryV
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
опять об указателях на процедуру - не работает код
Насколько я помню, отдельная процедура и метод в классе - несколько разные вещи, друг с другом не совместные...
И меня смущает вызов конструктора TMyClass - все ли там в порядке с адресами...

Удачи
...
Рейтинг: 0 / 0
20.08.2003, 10:46
    #32241330
ZrenBy
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
опять об указателях на процедуру - не работает код
>>MyObject := TMyClass.Create(@TForm1.SomeWork);

1. @Form1.SomeWork ?

2. MakeObjectInstance ?
...
Рейтинг: 0 / 0
20.08.2003, 11:00
    #32241370
StarWind
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
опять об указателях на процедуру - не работает код
адрес процедуры класса удобно получать при помощи функции GetProcAddress
правда тогда надо объявлять класс как наследник TObject
...
Рейтинг: 0 / 0
20.08.2003, 11:01
    #32241374
StarWind
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
опять об указателях на процедуру - не работает код
сорри, GetMethodAddress
...
Рейтинг: 0 / 0
20.08.2003, 11:18
    #32241433
Guilty
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
опять об указателях на процедуру - не работает код
2 ZrenBy:
я не понял, это вопросы или рекомендации?
Если рекомендации, то
1) так не работает(не компилируется),
2) хм, не понял?
Если вопросы:
1) берем адрес процедуры ;)
2) хм, не понял? ;)

2 StarWind
Ну, во-первых, если явно предок не указан, то по умолчанию он TObject,
во-вторых, ты уверен, что такая функция есть (GetmethodAddress)? А потом, а чем плоха такая запись @...?
...
Рейтинг: 0 / 0
20.08.2003, 11:50
    #32241534
ZrenBy
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
опять об указателях на процедуру - не работает код
Значит так

1. SomeWork делаешь глобальной
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
var Form1: TForm1;
procedure SomeWork;
...
procedure SomeWork;
begin
  ...
end;

2.

>>procedure TForm1.FormCreate(Sender: TObject);
>>begin
>> MyObject := TMyClass.Create(@TForm1.SomeWork);
>>end;

Это перенеси из FormCreate в FormShow
Код: plaintext
1.
2.
3.
4.
procedure TForm1.FormShow(Sender: TObject);
begin
  MyObject := TMyClass.Create(@SomeWork);
end;


Усё будет работать
...
Рейтинг: 0 / 0
20.08.2003, 12:37
    #32241639
anubis
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
опять об указателях на процедуру - не работает код
Ссылки на метод объекта не эквивалентны ссылкам на процедуру.

Ссылка на процедуру представляет собой 32 битный указатель на место расположения машинного кода процедуры в сегменте кода программы, т.е. число.

Ссылка на метод объекта - пара чисел, первое из которых - адрес кода самого метода в классе объекта; а второе - адрес объекта, для которого вызывается метод. Для хранения ссылок на метод в DelphiPascal существует тип TMethod:
Код: plaintext
1.
2.
3.
4.
  type
  TMethod = record
    Code, Data: Pointer;
  end;


Метод TObject.MethodAddress возвращает как раз значение соответствующее TMethod.Code.

Т.о., чтобы сохранить где-то ссылку на метод объекта, необходимо объявить переменную типа TMethod, и заполнить ее поля:
Код: plaintext
1.
2.
3.
4.
5.
6.
var MyMethod: TMethod;

...

MyMethod.Data := Pointer(SomeObjectHasMethod);
MyMethod.Code := SomeObjectHasMethod.MethodAddress('SomeUsefullMethod');


(кстати значение Event`ов представляют собой тоже TMethod, только типизированные )


Чтобы использовать сохраненное ранее в MyMethod значение для вызова метода, необходимо объявить в секции деклараций событийный тип соответствующий структуре сохраненного метода и привести к нему MyMethod.

Например:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TUsefulMethod = procedure (Param: String) of object;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    MyMethod: TMethod;
  public
    { Public declarations }
    procedure StoreMethod(MethodIndex: Integer);
    procedure CallStoredMethod();
  published
    procedure UsefulMethod1(Param: String);
    procedure UsefulMethod2(Param: String);
  end;

var
  Form1: TForm1;

implementation

uses StrUtils;

{$R *.dfm}

{ TForm1 }

procedure TForm1.CallStoredMethod;
begin
  if (MyMethod.Data <> nil) and (MyMethod.Code <> nil) then
  begin
    TUsefulMethod(MyMethod)('text for caption');
  end;//if
end;

procedure TForm1.StoreMethod(MethodIndex: Integer);
begin
  MyMethod.Data := Self;
  if MethodIndex =  0  then
    MyMethod.Code := MethodAddress('UsefulMethod1')
  else
    MyMethod.Code := MethodAddress('UsefulMethod2');
end;

procedure TForm1.UsefulMethod1(Param: String);
begin
  Caption := Param;
end;

procedure TForm1.UsefulMethod2(Param: String);
begin
  Caption := ReverseString(Param);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  { меняем метод который вызывается при нажатии кнопки }
  if (Tag =  0 ) then Tag :=  1  else Tag :=  0 ;
  StoreMethod(Tag);
  CallStoredMethod;
end;

end.


Обрати внимание, что UsefulMethod1 и UsefulMethod2 объявлены как published, иначе MethodAddress возвратит nil;
...
Рейтинг: 0 / 0
20.08.2003, 13:25
    #32241738
Дмитрий Мыльников
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
опять об указателях на процедуру - не работает код
Вообще то для методов объектов (процедур и функций) нужно добавлять of object

Код: plaintext
1.
2.
type
  TMyMethod = procedure of object;


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

Далее, не изгаляйтесь со всеми этими указателями и т.п. Зачем же вы тогда процедурный тип объявляете? Используйте его на прямую и большей части проблем не будет. В итоге должно быть что-то типа:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
unit Unit1;

interface

uses
  ...
type
  TMyMethod = procedure of object;

  TMyClass = class
  private
    Method:TMyMethod;
  public
    constructor Create(aMethod:TMyMethod);
    procedure MyProc;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    MyObject: TMyClass;

    procedure SomeWork; 
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  MyObject := TMyClass.Create(SomeWork);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  MyObject.Free;
end;

procedure TForm1.SomeWork;
begin
  Memo1.Lines.Append('ttt');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  MyObject.MyProc;
end;

{ TMyClass }

constructor TMyClass.Create(aMethod:TMyMethod);
begin
  Method := aMethod;
end;

procedure TMyClass.MyProc;
begin
  if assigned(Method) then Method;
end;

end.



Конструкция if assigned обычно всегда используется в текстах самой Borland. Правда, у них обычно изначально в конструкторе Method:=NIL; а присвоение конкретной процедуры происходит позже. Это позволяет исключить ошибку доступа, если метод не поределён. Кстати, именно через этот механизм работают события. А удобно это тем, что можно по ходу программы изменять реакцию объектов, а можно и вообще её "выключить", если опять присвоить NIL.
...
Рейтинг: 0 / 0
20.08.2003, 13:28
    #32241745
Guilty
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
опять об указателях на процедуру - не работает код
Теперь все прояснилось, спасибо всем, особенно ZrenBy и anubis
...
Рейтинг: 0 / 0
20.08.2003, 13:31
    #32241748
Guilty
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
опять об указателях на процедуру - не работает код
.. и Дмитрию Мыльникову тоже :))
...
Рейтинг: 0 / 0
21.08.2003, 03:51
    #32242500
StarWind
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
опять об указателях на процедуру - не работает код
сорри за опаздание, но по умолчанию не подставляется нкакой родитель класса. Потому как вполне возможен класс без родителей
...
Рейтинг: 0 / 0
Форумы / Delphi [игнор отключен] [закрыт для гостей] / опять об указателях на процедуру - не работает код / 12 сообщений из 12, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]