Дональд Бокс - Сущность технологии СОМ. Библиотека программиста Страница 40

Тут можно читать бесплатно Дональд Бокс - Сущность технологии СОМ. Библиотека программиста. Жанр: Компьютеры и Интернет / Программирование, год -. Так же Вы можете читать полную версию (весь текст) онлайн без регистрации и SMS на сайте «WorldBooks (МирКниг)» или прочесть краткое содержание, предисловие (аннотацию), описание и ознакомиться с отзывами (комментариями) о произведении.
Дональд Бокс - Сущность технологии СОМ. Библиотека программиста

Дональд Бокс - Сущность технологии СОМ. Библиотека программиста краткое содержание

Прочтите описание перед тем, как прочитать онлайн книгу «Дональд Бокс - Сущность технологии СОМ. Библиотека программиста» бесплатно полную версию:
В этой книге СОМ исследуется с точки зрения разработчика C++. Написанная ведущим специалистом по модели компонентных объектов СОМ, она раскрывает сущность СОМ, помогая разработчикам правильно понять не только методы модели программирования СОМ, но и ее основу. Понимание мотивов создания СОМ и ее аспектов, касающихся распределенных систем, чрезвычайно важно для тех разработчиков, которые желают пойти дальше простейших приложений СОМ и стать по-настоящему эффективными СОМ-программистами. Показывая, почему СОМ для распределенных систем (Distributed СОМ) работает именно так, а не иначе, Дон Бокс дает вам возможность применять эту модель творчески и эффективно для ежедневных задач программирования.

Дональд Бокс - Сущность технологии СОМ. Библиотека программиста читать онлайн бесплатно

Дональд Бокс - Сущность технологии СОМ. Библиотека программиста - читать книгу онлайн бесплатно, автор Дональд Бокс

STDMETHODIMP CarPlane::QueryInterface(REFIID riid, void **ppv)

{

if (riid == IID_IUnknown) *ppv = static_cast<IUnknown*>(&m_xCar);

else if (riid == IID_IVehicle) *ppv = static_cast<IVehicle*> (&m_xCar);

else if (riid == IID_ICar) *ppv = static_cast<ICar*>(&m_xCar);

else if (riid == IID_IPlane) *ppv = static_cast<IPlane*>(&m_xPlane);

else return (*ppv = 0), E_NOINTERFACE;

((IUnknown*)(*ppv))->AddRef();

return S_OK;

}

Для обеспечения идентификации объекта каждый из элементов данных CarPlane должен или воспроизвести этот код в своей собственной реализации QueryInterface, или просто передать управление главной функции QueryInterface в CarPlane. Чтобы осуществить это, необходим механизм перехода к главному объекту со стороны функции-члена составного элемента данных. Определение класса CarPlane::XCar содержит встроенную подпрограмму, которая использует фиксированные смещения для вычисления указателя this главного объекта от указателя this составного элемента данных.

inline CarPlane CarPlane::XCar::This(void)

{

return (CarPlane*)((char*)this

// ptr to composite – указатель на композит – offsetof (CarPlane, m_xCar)); }

inline CarPlane CarPlane::XPlane::This(void)

{

return (CarPlane*)((char*)this

// ptr to composite – указатель на композит

– offsetof(CarPlane, m_xPlane));

}

Такая технология вычисления обратного указателя (back-pointer) компактна и чрезвычайно эффективна, так как не требует явных элементов данных для нахождения главного объекта внутри реализации метода элемента данных. При наличии таких алгоритмов вычисления обратного указателя реализация композитного QueryInterface становится тривиальной:

STDMETHODIMP CarPlane::XCar::QueryInterface(REFIID r, void**p)

{

return This()->QueryInterface(r, p);

}

STDMETHODIMP CarPlane::XPlane::QueryInterface(REFIID r, void**p)

{

return This()->QueryInterface(r, p);

}

Такая же передача this потребуется для AddRef и Release для получения обобщенного представления о времени жизни объекта в случае составных (композитных) элементов данных.

Технология, основанная на использовании композиции для реализации интерфейсов, требует значительно больше кода, чем при простом множественном наследовании. Кроме того, качество генерированного кода, вероятно, не лучше (а возможно, и хуже), чем в случае множественного наследования. Из того факта, что классу CarPlane не понадобилось наследовать ни одному интерфейсу СОМ, следует, что композиция является разумной технологией для внесения СОМ в старые библиотеки классов. Например, MFC (Microsoft Foundation Classes – библиотека базовых классов Microsoft) использует эту технологию. Причиной применения композиции при реализации новых классов является получение отдельных реализации метода, определенного одинаково более чем в одном интерфейсе. К счастью, стандартные интерфейсы, определяемые СОМ, очень редко создают такие конфликты, а те немногие, которые создают, почти всегда преобразуются в семантически эквивалентные функции. Для разрешения коллизий, подобных тем, что произошли в сценарии с GetMaxSpeed , композиция, вероятно, и не требуется, так как в первом приближении для преобразования двойников в уникальные объекты достаточно использования промежуточных классов. Эта методика проста, эффективна и фактически не требует дополнительного кода. Основная причина использования композиции в новом коде заключается в том, что нужно обеспечить подсчет ссылок в каждом интерфейсе.

Иногда желательно разместить ресурсы в объекте на базе уже использующихся интерфейсов. В то же время из использования множественного наследования для реализации интерфейсов СОМ следует, что в каждой таблице vtbl будет использована только одна реализация AddRef и Release. Хотя можно выявить первый запрос на заданный интерфейс и разместить ресурсы по требованию:

STDMETHODIMP QueryInterface(REFIID riid, void **ppv)

{

if (riid == IID_IBoat)

{

// allocate resource the first time through

// размещаем ресурсы при первом проходе

if (m_pTonsOfMemory == 0) m_pTonsOfMemory = new char[4096 * 4096];

*ppv = static_cast<IBoat*>(this);

}

else if

}

не существует способа определить момент, когда больше нет внешних указателей интерфейса IBoat, так как вызов Release, который клиент делает через интерфейс IBoat, неотличим от вызова Release, сделанного через любой другой интерфейс объекта. В обычной ситуации именно это и нужно, но в данном случае вызовы AddRef и Release через интерфейсы IBoat необходимо рассматривать иначе. Если бы интерфейс IBoat был реализован с использованием композиции, то он имел бы свои собственные уникальные реализации AddRef и Release, в которых он мог бы поддерживать свой собственный счетчик ссылок, отличный от счетчика главного объекта:

class CarBoatPlane : public ICar, public IPlane

{

LONG m_cRef;

char *m_pTonsOfMemory;

CarBoatPlane (void) : m_cRef(0),

m_pTonsOfMemory (0) {}

public:

// IUnknown methods – методы IUnknown

STDMETHODIMP QueryInterface(REFIID, void**);

STDMETHODIMP_(ULONG) AddRef(void);

STDMETHODIMP_(ULONG) Release(void);

// IVehicle methods – методы IVehicle

STDMETHODIMP GetMaxSpeed(long *pMax);

// ICar methods – методы ICar

STDMETHODIMP Brake(void);

// IPlane methods – методы IPlane

STDMETHODIMP TakeOff(void);

// define nested class that implements IBoat

// определяем вложенный класс, реализующий IBoat

struct XBoat : public IBoat {

// get back pointer to main object

// получаем обратный указатель на главный объект

inline CarBoatPlane* This();

LONG m_cBoatRef;

// per-interface ref count

// счетчик ссылок на каждый интерфейс

XBoat(void) : m_cBoatRef(0) {}

STDMETHODIMP QueryInterface(REFIID, void**);

STDMETHODIMP_(ULONG) AddRef(void);

STDMETHODIMP_(ULONG) Release(void);

STDMETHODIMP GetMaxSpeed(long *pval);

STDMETHODIMP Sink(void);

};

XBoat m_xBoat; };

Реализация AddRef и Release из IBoat могут теперь следить за числом ссылок типа IBoat и высвободить ресурсы, когда они больше не нужны:

STDMETHODIMP_(ULONG) CarBoatPlane::XBoat::AddRef()

{

ULONG res = InterlockedIncrement(&m_cBoatRef);

if (res == 1)

{

// first AddRef – первый AddRef

// allocate resource and forward AddRef to object

// размещаем ресурсы и пересылаем AddRef на объект

This()->m_pTonsOfMemory = new char[4096*4096];

This()->AddRef(); }

return res; }

STDMETHODIMP_(ULONG) CarBoatPlane::XBoat::Release()

{

ULONG res = InterlockedDecrement(&m_cBoatRef);

if (res == 0) {

// last Release – последний Release

// free resource and forward Release to object

// освобождаем ресурсы и пересылаем Release на объект

delete [] This()->m_pTonsOfMemory;

This()->Release();

} return res; }

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

((IUnknown*)(*ppv))->AddRef();

// use exact ptr

// используем точный указатель return S_OK;

вместо такого:

AddRef();

// just call this->AddRef

// только вызов

this->AddRef return S_OK;

Первый вариант гарантирует, что если клиент пишет следующий правильный код

IBoat *pBoat = 0;

HRESULT hr = pUnk->QueryInterface(IID_IBoat, (void**)&pBoat);

if (SUCCEEDED(hr))

{ hr = pBoat->Sink(); pBoat->Release(); }

то для AddRef и для Release обязательно будет использовано одно и то же значение указателя.

Можно осуществлять композицию в контексте управляемой таблицами реализации QueryInterface. При наличии семейства макросов препроцессора, показанного в предыдущей главе, достаточно всего одного дополнительного макроса, чтобы определить, что вместо базового класса используется элемент данных, и второго макроса, чтобы реализовать методы IUnknown в композите:

class CarBoatPlane : public ICar, public IPlane

{ public: struct XBoat : public IBoat

{

// composite QI/AddRef/Release/This()

// композит из QI/AddRef/Release/This()

IMPLEMENT_COMPOSITE_UNKNOWN(CarBoatPlane, XBoat, m_xBoat) STDMETHODIMP GetMaxSpeed(long *pval);

STDMETHODIMP Sink(void);

};

XBoat m_xBoat;

// IVehicle methods

// методы IVehicle

STDMETHODIMP GetMaxSpeed(long *pMax);

// ICar methods

// методы ICar

STDMETHODIMP Brake(void);

// IPlane methods

// методы IPlane

STDMETHODIMP TakeOff(void);

// standard heap-based QI/AddRef/Release

// стандартные расположенные в «куче» QI/AddRef/Release

IMPLEMENT_UNKNOWN(CarBoatPlane)

BEGIN_INTERFACE_TABLE(CarBoatPlane)

IMPLEMENTS_INTERFACE_AS(IVehicle, ICar)

IMPLEMENTS_INTERFACE(ICar)

IMPLEMENTS_INTERFACE(IPlane)

// macro that calculates offset of data member

// макрос, вычисляющий смещение элемента данных

IMPLEMENTS_INTERFACE_WITH_COMPOSITE(IBoat, XBoat, m_xBoat)

END_INTERFACE_TABLE() };

В приведенном выше определении класса опущены только определения методов объекта вне QueryInterfасе, AddRef и Release. Два новых макроса, использованных в определении класса, определяются следующим образом:

// inttable.h

// (book-specific header file)

// (заголовочный файл, специфический для данной книги)

#define COMPOSITE_OFFSET(ClassName, BaseName, \

MemberType, MemberName) \

(DWORD(static_cast<BaseName*>(\

reinterpret_cast<MemberType*>(0x10000000 + \

offsetof(ClassName, MemberName)))) – 0х10000000)

#define IMPLEMENTS_INTERFACE_WITH_COMPOSITE(Req,\

MemberType, MemberName) \

{ &IID_##Req,ENTRY_IS_OFFSET, COMPOSITE_OFFSET(_IT,\

Req, MemberType, MemberName) },

// impunk.h

// (book-specific header file)

// (заголовочный файл, специфический для данной книги)

Перейти на страницу:
Вы автор?
Жалоба
Все книги на сайте размещаются его пользователями. Приносим свои глубочайшие извинения, если Ваша книга была опубликована без Вашего на то согласия.
Напишите нам, и мы в срочном порядке примем меры.
Комментарии / Отзывы
    Ничего не найдено.