Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2006.08.20;
Скачать: CL | DM;

Вниз

Создание массива констант из указателей method pointers   Найти похожие ветки 

 
Fosgen   (2006-07-29 10:55) [0]

Вопрос в следующем:
1. Есть класс TObject1
2. У этого класса имеется куча отднотипных методов (около 100), использующих соответственно поля и св-ва класса TObject1
3. Есть класс TObject2, имеющий св-во OnEvent, по типу совпадающее с методами класса TObject1
4. В процессе работы создается один объект класса TObject1, и много-много объектов класса TObject2. При создании множества объектов класса TObject2, им надо назначать в св-во OnEvent обработчики из класса TObject1.
5. Для этого счастья я планировал завести массив констант. Где будут храниться указатели на методы-обработчики класса TObject1... НО...

1) Создать такой массив на этапе компиляции не представляется возможным, поскольку адреса объекта класса TObject1 еще нет и сл-но адеса его методов также не известны. Дельфя ругается.
2) Массив получается создать если предварительно объявить процедурный тип навроде TNotifyEvent и задать все методы-обработчики класса TObject1  как class procedure, но в этом случае они теряют доступ к св-вам и полям класса TObject1. Почему? Да фиг его знает... Я так этого и не понял, пробовал работать на уровне private\public\published - не помогает.
3) Была идея создать массив переменных где формировать указатели типа method pointers на методы-обработчики класса TObject1. Но в Дельфи я не обнаружил ни одной функции, по имени (либо еще как-то) возвращающей указатель на метод, а не просто нетипизированный указатель. Последний - никуда не впился...

В общем я свои идеи исчерпал, через case ... of либо if ... then делать присвоения 100 различных вариантов меня как-то ломает... Ну как-то тупо это получается...
Пожалуйста, подскажите что в таком случае можно сделать, либо есть какой-то вариант решения?


 
Loginov Dmitry ©   (2006-07-29 16:35) [1]

Букв слишком много...


 
Пусик ©   (2006-07-29 16:58) [2]


> Loginov Dmitry ©   (29.07.06 16:35) [1]
> Букв слишком много...


Ну так не читай.


> Fosgen   (29.07.06 10:55) 


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


 
tesseract ©   (2006-07-29 17:37) [3]

> 3) Была идея создать массив переменных где формировать указатели
> типа method pointers на методы-обработчики класса TObject1.
> Но в Дельфи я не обнаружил ни одной функции, по имени (либо
> еще как-то) возвращающей указатель на метод, а не просто
> нетипизированный указатель. Последний - никуда не впился...


а через typecasting?

только процедырный тип должен быть один на всех.

а не проще TObject2 от Tobject1 унаследовать? и потом assign-ить?


 
Fosgen   (2006-07-29 20:56) [4]

2: Пусик ©   (29.07.06 16:58) [2]
Значит так по вариантам:

В одном модуле (всегда):

type TObject2 = class
 private
 public
     Event: Byte;
     FonClick: TGLNotify;
 published
   property onClick: TGLNotify read FonClick write FonClick;
 end;


В другом модуле (module2):

Вариант № 1:


Uses module2;

type TObject1 = class
   FrameCount: Word;
   Visible: Boolean;
   Elements: array of TObject2;
 private
   procedure CheckBoxChange(Sender: TGLWidget);
   procedure CheckChange(Sender: TGLWidget);
   procedure NoAction(Sender: TGLWidget);
   procedure ChgHullCond(Sender: TGLWidget);
end;

const
    EventHandlers: array[0..100]  of TGLNotify =
    (NoAction, ChgHullCond, ChgHullCondT, ....


Вот на последнюю строку она и ругается. В принципе понятно почему.

Вариант № 2:


Uses module2;

type TGLNotify = procedure(Sender: TGLWidget) of object;

type TObject1 = class
 public
   FrameCount: Word;
   Visible: Boolean;
   Elements: array of TObject2;
 published
   class procedure CheckBoxChange(Sender: TGLWidget);
   class procedure CheckChange(Sender: TGLWidget);
   class procedure NoAction(Sender: TGLWidget);
   class procedure ChgHullCond(Sender: TGLWidget);
end;

const
    EventHandlers: array[0..100]  of TGLNotify =
    (TObject1.NoAction, TObject1.ChgHullCond, TObject1.ChgHullCondT, ....


В этом варианте из обработчиков TObject1.NoAction, TObject1.ChgHullCond и т.д. не видно св-в FrameCount: Word; Visible: Boolean и т.д.

2: tesseract ©   (29.07.06 17:37) [3]
а не проще TObject2 от Tobject1 унаследовать? и потом assign-ить?

Нет не проще. И совсем из другой оперы. Зачем мне допустим делать кнопку потомком формы (как пример)? А обработчики формы-то надо присваивать на события кнопки...


 
Fosgen   (2006-07-29 21:01) [5]

Косяк мой. В указанном коде во втором варианте строку:

type TGLNotify = procedure(Sender: TGLWidget) of object;

надо перенести в module2, ну и класс TGLWidget считать равным TObject2 это одно и то же, только заменить забыл...


 
jack128 ©   (2006-07-30 17:19) [6]

Fosgen   (29.07.06 10:55)
1) Создать такой массив на этапе компиляции не представляется возможным, поскольку адреса объекта класса TObject1 еще нет и сл-но адеса его методов также не известны.

адреса как раз известны.  P := @TObject.SameMethod. Нно событие - это не только адрес метода, но и указатель на собственно объект.  А этого вот этого объекта на этапе компиляции еще нет.

Fosgen   (29.07.06 10:55)
все методы-обработчики класса TObject1  как class procedure, но в этом случае они теряют доступ к св-вам и полям класса TObject1. Почему?

потому что ты они стали классовыми методами. То есть связанными  с классом, а конкретным экземпляром этого класса(читай с объектом).

Как это делается:

var
 Obj1: TObject1;
 Handlers: array [0..100 - 1] of TNotifyEvent;
 Obj2Arr: array [0..100 - 1] of TObject2;
begin
 // Гдето выше объект Obj1 должен быть уже создан!!!!!!!!!!!!!
 Handlers[0] := Obj1.CheckBoxChange;
 Handlers[1] := Obj1.CheckChange;
 Handlers[2] := Obj1.NoAction;
 Handlers[3] := Obj1.ChgHullCond;
 // и так далее...
 for I := low(Obj2Arr) to high(Obj2Arr) do
 begin
   Obj2Arr[I] := TObject2.Create;
   Obj2Arr[I].SameEvent := Handlers[I];
 end;
end;


 
Fosgen   (2006-07-30 20:42) [7]

2: jack128 ©   (30.07.06 17:19) [6]

Уважаемый, а Вы внимательно читаете предыдущие посты?

адреса как раз известны.  P := @TObject.SameMethod. Нно событие - это не только адрес метода, но и указатель на собственно объект.  А этого вот этого объекта на этапе компиляции еще нет.

Вроде я еще в первом своем посте так и написал:
поскольку адреса объекта класса TObject1 еще нет и сл-но адеса его методов также не известны...

А т.к. Ваш указатель  P := @TObject.SameMethod является относительным TObject, то на выходе получите Вы только тип Pointer, который интереса в данном контексте никому не представляет.

То есть связанными  с классом, а конкретным экземпляром этого класса(читай с объектом).

И с каким именно классом\объектом (по примеру) они стали связаны?

Как это делается:

Знаете ояпть же, как написано в ПЕРВОМ посте: делать присвоения 100 различных вариантов меня как-то ломает

Если Вам такое решение нравится - милости прошу, но для меня это не является ответом Как это делается:. Потому что гибкости в таком подходе - никакой. Количество методов изменилось, процесс заполнения массива тоже переделывай... Да и зачем тогда массив-то? В чем его смысл? Через case...of сразу и присваивай методы. Тем более что Вы не поняли  - я не утверждал что Obj2Arr[I].SameEvent := Handlers[I]; Будет применимо всегда. Тем более что даже количество элементов Obj2Arr может различаться (превышать) с кол-вом элементов Handlers... И не всегда первый элемент Obj2Arr должен содержать ссылку на первый элемент Handlers.


 
Джо ©   (2006-08-01 05:02) [8]

Не уверен, что я верно понял задачу.
Вот что-то в таком роде подойдет?

 
...
 TObject2 = class
 private
   FOnEvent: TOnEventProc;
 public
   property OnEvent: TOnEventProc read FOnEvent write FOnEvent;
 end;

 TObject1 = class
 published
   procedure A (Sender: TObject2);
   procedure B (Sender: TObject2);
   procedure C (Sender: TObject2);
 end;

var
 Form1: TForm1;

implementation

{$R *.dfm}

const
 MethodNames: array [1..3] of string =
 (
   "A",
   "B",
   "C"
 );

var
 Objects2: array [1..3] of TObject2;
 Object1: TObject1;

procedure CreateAssignEventHandlers;
var
 I: Integer;
 MP: Pointer;
 Met: TMethod;
begin
 for I := Low(Objects2) to High (Objects2) do
 begin
   Objects2[I] := TObject2.Create;
   MP := Object1.MethodAddress(MethodNames[I]);
   Met.Code := MP;
   Met.Data := Objects2[I];
   Objects2[I].OnEvent := TOnEventProc(Met);
 end;
end;

{ TObject1 }

procedure TObject1.A(Sender: TObject2);
begin
 ShowMessage ("A")
end;

procedure TObject1.B(Sender: TObject2);
begin
 ShowMessage ("B")
end;

procedure TObject1.C(Sender: TObject2);
begin
 ShowMessage ("C")
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 I: Integer;
begin
 // проверка
 for I := Low(Objects2) to High (Objects2) do
   Objects2[I].OnEvent (Objects2[I]);
end;

initialization
 Object1 := TObject1.Create;
 CreateAssignEventHandlers;

finalization
 // ну, тут удаляем все объекты, это я опустил

end.


 
Джо ©   (2006-08-01 05:03) [9]

Перед этим кодом, разумеется:
type
 TObject2 = class;

 TOnEventProc = procedure (Sender: TObject2) of object;


 
Джо ©   (2006-08-01 05:05) [10]

Всё, точно спать пора... В процедуре CreateAssignEventHandlers вот так:

   Met.Code := MP;
   Met.Data := Object1;


 
Fosgen   (2006-08-01 20:14) [11]

Однако!
Благодарствую, Джо!

Решение, предложенное тобой, по крайней мере удовлетворяет все вышеизложенные конфликты. Проверить корректность работы структуры

MP := Object1.MethodAddress(MethodNames[I]);
  Met.Code := MP;
  Met.Data := Objects2[I];
  Objects2[I].OnEvent := TOnEventProc(Met);


Еще не успел, но как минимум все компилируется! Т.е. нет несовпадения типов и нет проблем с доступом к полям и переменным.
А я как раз и не мог найти тип, способный хранить двойной указатель на метод. Оказывается есть прямо TMethod...

Да, и откуда ты выкопал такую структуру: TOnEventProc(Met);??? Очень интересно, даже нигде намека не встречал... Но компилируется, млин!!!
Спасибо!!!


 
Джо ©   (2006-08-01 21:02) [12]

> Проверить корректность работы структуры
>
>  Met.Data := Objects2[I];

Тут я допустил машинальную ошибку. Исправился в посте [10] :)


 
DiamondShark ©   (2006-08-01 23:24) [13]

{$M+}
 TObject1 = class
 published
   procedure A (Sender: TObject2);
   procedure B (Sender: TObject2);
   procedure C (Sender: TObject2);
 end;
{$M-}


 
Джо ©   (2006-08-01 23:37) [14]

> [13] DiamondShark ©   (01.08.06 23:24)
> {$M+}

Оно для MethodAddress не нужно :)


 
DiamondShark ©   (2006-08-01 23:53) [15]

И ведь правда не нужно. :-Х


 
Fosgen   (2006-08-02 09:58) [16]

2: Джо ©   (01.08.06 21:02) [12]

Да там я разумеется исправил как должно быть. В конце концов, если я с такими вопросами на форум выпадаю, то думаю - не безнадежно тупой и уж основы-то знаю. :)

Однако, решение искалось почти две недели, за последнюю было "опрошено" около 10 программеров в живую, и сколько-то человеков просмотрело вопрос на форуме, а предложить настоящее решение смог только один... Мда...
Спасибо, Джо!



Страницы: 1 вся ветка

Текущий архив: 2006.08.20;
Скачать: CL | DM;

Наверх




Память: 0.53 MB
Время: 0.04 c
2-1154080034
Sank
2006-07-28 13:47
2006.08.20
опять кодировка


2-1154512267
Barnikle
2006-08-02 13:51
2006.08.20
найти слово в тексте


15-1153732118
Ketmar
2006-07-24 13:08
2006.08.20
как запихнуть в образ MDF ещё один файлик?


6-1144128262
CyMKuH
2006-04-04 09:24
2006.08.20
FTP клиент


1-1151931242
Шурик
2006-07-03 16:54
2006.08.20
Работа с MSWord