Большой архив статей, книг, документации по программированию, вебдизайну, компьютерной графике, сетям, операционным системам и многому другому
 
<Добавить в Избранное>    <Сделать стартовой>    <Реклама на сайте>    <Контакты>
  Главная Документация Программы Обои   Экспорт RSS E-Books
 
 

   Программирование -> C/C++ -> Сущность технологии COM


Оптимизация QueryInterface

Фактически реализация QueryInterface, показанная ранее в этой главе, очень проста и легко может поддерживаться любым программистом, имеющим хоть некоторое представление о СОМ и C++. Тем не менее, многие среды и каркасы приложений поддерживают реализацию, управляемую данными. Это помогает достичь большей расширяемости и эффективности благодаря уменьшению размера кода. Такие реализации предполагают, что каждый совместимый с СОМ класс предусматривает таблицу, которая отображает каждый поддерживаемый IID на какой-нибудь аспект объекта, используя фиксированные смещения или какие-то другие способы. В сущности, реализация QueryInterface, приведенная ранее в этой главе, строит таблицу, основанную на скомпилированном машинном коде для каждого из последовательных операторов if, а фиксированные смещения вычисляются с использованием оператора static_cast (static_cast просто добавляет смещение базового класса, чтобы найти совместимый с типом указатель vptr).

Чтобы реализовать управляемый таблицей QueryInterface, необходимо сначала определить, что эта таблица будет содержать. Как минимум, каждый элемент таблицы должен содержать указатель на IID и некое дополнительное состояние, которое позволит реализации найти указатель vptr объекта для запрошенного интерфейса. Хранение указателя функции в каждом элементе таблицы придаст этому способу максимальную гибкость, так как это даст возможность добавлять новые методики поиска интерфейсов к обычному вычислению смещения, которое используется для приведения к базовому классу. Исходный код в приложении к данной книге содержит заголовочный файл inttable.h, который определяет элементы таблицы интерфейсов следующим образом:

// inttable.h (book-specific header file)
// inttable.h (заголовочный файл, специфический для этой книги) 
// typedef for extensibility function 
// typedef для функции расширяемости 

typedef HRESULT (*INTERFACE_FINDER) 
(void *pThis, DWORD dwData, REFIID riid, void **ppv); 

// pseudo-function to indicate entry is just offset 
// псевдофункция для индикации того, что запись просто 
// является смещением 

#define ENTRY_IS_OFFSET INTERFACE_FINDER(-1) 
// basic table layout 
// представление базовой таблицы 

typedef struct INTERFACE_ENTRY { 
    const IID * pIID; 
      // the IID to match 
      // соответствующий IID 
    INTERFACE_FINDER pfnFinder; 
      // функция finder 
    DWORD dwData; 
      // offset/aux data
      // данные по offset/aux 
} INTERFACE_ENTRY; 

Заголовочный файл также содержит следующие макросы для создания интерфейсных таблиц внутри определения класса:

// Inttable.h (book-specific header file)
// Inttable.h (заголовочный файл, специфический для данной книги) 

#define BASE_OFFSET(ClassName, BaseName) \ 
(DWORD(static_cast<BaseName*>(reinterpret_cast\ 
<ClassName*>(0x10000000))) - 0х10000000) 

#define BEGIN_INTERFACE_TABLE(ClassName) \ 
typedef ClassName _ITCls;\ 
const INTERFACE_ENTRY *GetInterfaceTable(void) {\ 
static const INTERFACE_ENTRY table [] = {\ 

#define IMPLEMENTS_INTERFACE(Itf) \ 
{&IID_##Itf,ENTRY_IS_OFFSET,BASE_OFFSET(_ITCls,Itf)}, 

#define IMPLEMENTS_INTERFACE_AS(req, Itf) \ 
{&IID_##req,ENTRY_IS_OFFSET, BASE_OFFSET(_ITCls, Itf)}, 

#define END_INTERFACE_TABLE() \ 
{ 0, 0, 0 } }; return table; } 

Все, что требуется, - это стандартная функция, которая может анализировать интерфейсную таблицу в ответ на запрос QueryInterface. Такая функция содержится в файле Inttable.h:

// inttable.cpp (book-specific source file)
// inttable.h (заголовочный файл, специфический для данной книги) 
HRESULT InterfaceTableQueryInterface(void *pThis, 
                                     const INTERFACE_ENTRY *pTable, 
                                     REFIID riid, void **ppv) 
{ 
    if (InlineIsEqualGUID(riid, IID_IUnknown)) { 
        // first entry must be an offset 
        // первый элемент должен быть смещением 
        *ppv = (char*)pThis + pTable->dwData; 
        ((Unknown*) (*ppv))->AddRef () ; // A2 
        return S_OK; 
    } 
    else { 
        HRESULT hr = E_NOINTERFACE; 
        while (pTable->pfnFinder) { // null fn ptr == EOT 
            if (!pTable->pIID || InlineIsEqualGUID(riid,*pTable->pIID)) { 
                if (pTable->pfnFinder == ENTRY_IS_OFFSET) { 
                    *ppv = (char*)pThis + pTable->dwData; 
                    ((IUnknown*)(*ppv))->AddRef(); // A2 
                    hr = S_OK; 
                    break; 
                } 
                else { 
                    hr = pTable->pfnFinder(pThis, pTable->dwData, riid, ppv); 
                    if (hr == S_OK) break; 
                } 
             } 
             pTable++; 
        } 
        if (hr != S_OK) *ppv = 0; 
        return hr; 
    } 
} 

Получив указатель на запрошенный объект, InterfaceTableQueryInterface сканирует таблицу в поисках элемента, соответствующего запрошенному IID, и либо добавляет соответствующее смещение, либо вызывает соответствующую функцию. Приведенный выше код использует усовершенствованную версию IsEqualGUID, которая генерирует несколько больший код, но результаты по скорости примерно на 20-30 процентов превышают данные по существующей реализации, которая не управляется таблицей. Поскольку код для InterfaceTableQueryInterface появится в исполняемой программе только один раз, это весьма неплохой компромисс.

Очень легко автоматизировать поддержку СОМ для любого класса C++, основанную на таком табличном управлении, простым использованием С-препроцессора. Следующий фрагмент из заголовочного файла impunk.h определяет QueryInterface, AddRef и Release для объекта, использующего интерфейсные таблицы и расположенного в динамически распределяемой области памяти:

// impunk.h (book-specific header file)
// impunk.h (заголовочный файл, специфический для данной книги) 
// AUTO_LONG is just a long that constructs to zero 
// AUTO_LONG - это просто long, с конструктором, 
// устанавливающим значение в О 

struct AUTO_LONG { 
    LONG value; 
    AUTO_LONG (void) : value (0) {} 
}; 

#define IMPLEMENT_UNKNOWN(ClassName) \ 
AUTO_LONG m_cRef;\ 
STDMETHODIMP QueryInterface(REFIID riid, void **ppv){\ 
return InterfaceTableQueryInterface(this,\ 
GetInterfaceTable(), riid, ppv);\ 
}\ 
STDMETHODIMP_(ULONG) AddRef(void) { \ 
return InterlockedIncrement(&m_cRef.value); \ 
}\ 
STDMETHODIMP_(ULONG) Release(void) {\ 
ULONG res = InterlockedDecrement(&m_cRef.value) ;\ 
if (res == 0) \ 
delete this;\ 
return res;\ 
}\ 

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

Для реализации примера PugCat, уже встречавшегося в этой главе, необходимо всего лишь удалить текущие реализации QueryInterface, AddRef и Release и добавить соответствующие макросы:

class PugCat : public IPug, public ICat {
  protected: 
    virtual ~PugCat(void); 
  public: 
    PugCat(void); 
      // IUnknown methods 
      // методы IUnknown 
    IMPLEMENT_UNKNOWN (PugCat) 
    BEGIN_INTERFACE_TABLE(PugCat) 
      IMPLEMENTS_INTERFACE(IPug) 
      IMPLEMENTS_INTERFACE(IDog) 
      IMPLEMENTS_INTERFACE_AS(IAnimal,IDog) 
      IMPLEMENTS_INTERFACE(ICat)
    END_INTERFACE_TABLE() 
      // IAnimal methods 
      // методы IAnimal 
    STDMETHODIMP Eat(void); 
      // IDog methods 
      // методы IDog 
    STDMETHODIMP Bark(void); 
      // IPug methods 
      // методы IPug 
    STDMETHODIMP Snore(void); 
      // ICat methods 
      // методы ICat 
    STDMETHODIMP IgnoreMaster(void); 
}; 

Когда используются эти макросы препроцессора, для поддержки IUnknown не требуется никакого дополнительного кода. Все, что осталось сделать, это реализовать текущие методы интерфейса, которые делают этот класс уникальным.

Типы данных
Атрибуты и свойства
Исключения
Где мы находимся?

 

 
Интересное в сети
 
10 новых программ
CodeLobster PHP Edition 3.7.2
WinToFlash 0.7.0008
Free Video to Flash Converter 4.7.24
Total Commander v7.55
aTunes 2.0.1
Process Explorer v12.04
Backup42 v3.0
Predator 2.0.1
FastStone Image Viewer 4.1
Process Lasso 3.70.4
FastStone Image Viewer 4.0
Xion Audio Player 1.0.125
Notepad GNU v.2.2.8.7.7
K-Lite Codec Pack 5.3.0 Full


Наши сервисы
Рассылка новостей. Подпишитесь на рассылку сейчас и вы всегда будете в курсе последних событий в мире информационных технологий.
Новостные информеры. Поставьте наши информеры к себе и у вас на сайте появится дополнительный постоянно обновляемый раздел.
Добавление статей. Если вы являетесь автором статьи или обзора на тему ИТ присылайте материал нам, мы с удовольствием опубликуем его у себя на сайте.
Реклама на сайте. Размещая рекламу у нас, вы получите новых посетителей, которые могут стать вашими клиентами.
 
Это интересно
 

Copyright © CompDoc.Ru
При цитировании и перепечатке ссылка на www.compdoc.ru обязательна. Карта сайта.
 
Rambler's Top100