На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! ПРАВИЛА РАЗДЕЛА · FAQ раздела Delphi · Книги по Delphi
Пожалуйста, выделяйте текст программы тегом [сode=pas] ... [/сode]. Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.
Следующие вопросы задаются очень часто, подробно разобраны в FAQ и, поэтому, будут безжалостно удаляться:
1. Преобразовать переменную типа String в тип PChar (PAnsiChar)
2. Как "свернуть" программу в трей.
3. Как "скрыться" от Ctrl + Alt + Del (заблокировать их и т.п.)
4. Как прочитать список файлов, поддиректорий в директории?
5. Как запустить программу/файл?
... (продолжение следует) ...

Вопросы, подробно описанные во встроенной справочной системе Delphi, не несут полезной тематической нагрузки, поэтому будут удаляться.
Запрещается создавать темы с просьбой выполнить какую-то работу за автора темы. Форум является средством общения и общего поиска решения. Вашу работу за Вас никто выполнять не будет.


Внимание
Попытки открытия обсуждений реализации вредоносного ПО, включая различные интерпретации спам-ботов, наказывается предупреждением на 30 дней.
Повторная попытка - 60 дней. Последующие попытки бан.
Мат в разделе - бан на три месяца...
Модераторы: jack128, D[u]fa, Shaggy, Rouse_
  
> ООП. Callback class в юните - как обратиться к элементам основного класса. , Запутался в трёх соснах ООП
    Что-то меня заклинило на вроде бы простом вопросе :)
    Итак, есть некая программа захвата видео, основанная на direct show. Она даже работает, но очень спагеттиобразно написана, поэтому стал разделять куски на отдельные классы с самостоятельным функционированием.

    Суть проблемы.
    В юните есть основной класс
    ExpandedWrap disabled
        TDSCapturer = class(TObject)
        private
        public
        end;

    И есть класс, методы которого вызываются callback'ом после получения каждого кадра
    ExpandedWrap disabled
        TVideoGrabCB = class(TComponent, ISampleGrabberCB)
        private
          function SampleCB(SampleTime: Double; pSample: IMediaSample): HResult; stdcall;
          function BufferCB(SampleTime: Double; pBuffer: PByte; BufferLen: longint): HResult; stdcall;
          function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
          function _AddRef: integer; stdcall;
          function _Release: integer; stdcall;
        end;

    Понятно, что захваченный кадр мне надо передать из функции обработки callback в основной класс. И вот тут я начинаю тупить.
    Простейший способ - разместить буфер в разделе implementation. И это работает. Но это решение мне совершенно не нравится, так как
    а) всё, что находится в implementation, автоматом доступно и базовой программе, которая этот юнит вызывает. Что мне совершенно не надо (чревато всевозможными глюками в случае, если вдруг совпадёт какое-нибудь имя, например).
    б) если мне вдруг потребуется создать несколько экземпляров основного класса TDSCapturer (скажем, организовать многоканальный захват), то находящееся в implementation будет общим для всех экземпляров класса. И понять - какой именно граф захвата положил в общий буфер очередной кадр - будет невозможно в принципе.

    То есть в идеале - буфер лежит в разделе private основного класса. Но как тогда к нему обратиться из callback метода совершенно другого класса?
    Тут видится два подхода - либо каким-то образом сделать коллбек класс "подклассом" основного класса (не знаю - возможно ли это в принципе?).
    Либо при создании экземпляра коллбек класса передавать ему ссылку на основной класс и дальше заполнять буфер через эту ссылку. Либо еще как-то? Что-то заклинило меня...
      Пока выкрутился так:
      Создаю callback'и с указанием self как owner. В самих callback пишу
      ExpandedWrap disabled
        with Owner as TDSCapturer do
        begin
        end;

      Ну и внутри обращаюсь к private переменным класса. Корректен ли такой подход? Или есть какой-то подход лучше?
        Иметь в классе поле для хранения связанного класса и заполнять его в конструкторе или через свойство - нормальная практика. Тот же Owner или Parent, скажем.

        ExpandedWrap disabled
          TVideoGrabCB = class(TComponent, ISampleGrabberCB)
            private
             FCapturer: TDSCapturer;
             ...


        >аким-то образом сделать коллбек класс "подклассом" основного класса
        В свежих версиях Delphi есть вложенные (nested) классы, но полей внешнего они не видят, так что пользы тут нет.
          Цитата An_private @
          Либо при создании экземпляра коллбек класса передавать ему ссылку на основной класс и дальше заполнять буфер через эту ссылку. Либо еще как-то? Что-то заклинило меня...

          Сразу предупреждаю, что не использую Delphi.
          Но поскольку вопрос структурный, это не важно.

          Возможен такой вариант:
          Допустим получили какие-то данные, хоть даже графические.
          Хоть в другом потоке.
          1. Создадим в памяти объект для их хранения (если нужно) и заполним его данными.
          2. Поместим указатель на объект в очередь. В общем случае - потокобезопасную.
          3. Пошлём сообщение диспетчеру, о том, что получено.
          (Например, заранее предусмотренное сообщение главному окну.
          4. Диспетчер запустит обработчик (или процедуру любого предусмотренного класса).
          Который извлечёт указатель на контейнер из очереди и дальше.. всё, что предусмотрено
          алгоритмом.
          5. Уничтожим не нужный уже контейнер.
          6. Чтобы не заниматься массовым созданием/уничтожением контейнеров в памяти, предусмотрим обратную
          очередь с указателями на уже созданные контейнеры.
          7. Тогда модифицируем 1-й пункт - прежде, чем создавать новый контейнер, проверим обратную
          очередь, на предмет наличия пустых контейнеров. Если есть - не будем создавать
          новый, будем использовать готовый.

          Как-то так.
          Сообщение отредактировано: ЫукпШ -
            Owner совершенно нормальная практика. Только логичнее наверно назвать его Destination - неочевидно, что класс коллбэка должен иметь владельца и какой класс у него должен быть.
            Доступ к private делать можно, но тут чисто субъективно я выделяю "юнит-доступные" поля в отдельный protected раздел с комментарием:
            ExpandedWrap disabled
              Tsample = class
              strict private
              ...
              protected // для доступа из других классов модуля
              ...
              public
              ...
              end

            Это держит структуру доступа в строгости и наглядности и облегчает обращение с кодом.

            Кстати, переменные в implementation как раз недоступны извне.
              Цитата MBo @
              Иметь в классе поле для хранения связанного класса и заполнять его в конструкторе или через свойство - нормальная практика.

              Ясно. То есть я в очередной раз изобрел велосипед и это работать будет :) Спасибо :)
              Цитата ЫукпШ @
              Чтобы не заниматься массовым созданием/уничтожением контейнеров в памяти, предусмотрим обратную
              очередь с указателями на уже созданные контейнеры.

              Ну, у меня классическая ситуация single writer - single reader. Поэтому у меня заранее создан массив на 250 кадров (10 секунд). В каждом кадре есть ссылка на кадровый буфер (выделяется сразу при создании экземпляра класса) и два boolean поля - used и filled. Соответственно, когда callback должен сбросить кадр - он ищет кадр с used=false, ставит в нём used в true и копирует в него данные. По завершению процесса ставит поле filled в true.
              Основной класс ищет кадры с filled=true, забирает из них данные, после чего ставит filled и used в false.
              0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
              0 пользователей:


              Рейтинг@Mail.ru
              [ Script execution time: 0,0334 ]   [ 16 queries used ]   [ Generated: 28.03.24, 20:46 GMT ]