Индивидуальное определение одноимённой функции под каждый интерфейс

Compositum
Дата: 20.05.2015 13:14:38
Доброго времени суток.

Несколько классов наследуются от IUnknown:

#include <objbase.h>

// pure abstract base class
interface IX : IUnknown{  
  virtual void __stdcall Fx() = 0; // pure virtual function
};

// pure abstract base class
interface IY : IUnknown{
  virtual void __stdcall Fy() = 0; // pure virtual function
};

// pure abstract base class
interface IZ : IUnknown{
  virtual void __stdcall Fz() = 0; // pure virtual function
};

extern "C"{
  extern const IID IID_IX;
  extern const IID IID_IY;
  extern const IID IID_IZ;
}


Эти интерфейсы я реализую в классе CA (см. код ниже).

Интерфейс IUnknown определяет три функции: QueryInterface, AddRef и Release. Мне нужно, чтобы реализация функции QueryInterface была общей для всех трёх интерфейсов (это успешно работает), а вот функции AddRef и Release должны быть определены индивидуально для каждого интерфейса (с этим как раз таки проблема).

Т.е. если переменная объявлена как IX, то для неё должны использоваться AddRef и Release именно те, которые я хочу определить для IX. Та же история по отношению и к IY, IZ. Это позволит мне вести подсчёт ссылок на компонент для каждого интерфейса (для удобства в отладке). Но синтаксис, который я записал - неверный (см. TODO в комментариях). Как это сделать правильно?

Текущий вариант кода класса CA такой:

//#include "../../std_lib_facilities.h"
#include <iostream>
#include "IFace.h"

// COM component
class CA :
  public IX,
  public IY,
  public IZ
{
public:
  CA();
  ~CA();
  virtual void __stdcall Fx(); // IX
  virtual void __stdcall Fy(); // IY
  virtual void __stdcall Fz(); // IZ
  // IUnknown
  virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv);
  virtual ULONG __stdcall IX::AddRef(); // for IX instances
  virtual ULONG __stdcall IX::Release(); // for IX instances

  virtual ULONG __stdcall IY::AddRef(); // for IY instances
  virtual ULONG __stdcall IY::Release(); // for IY instances

  virtual ULONG __stdcall IZ::AddRef(); // for IZ instances
  virtual ULONG __stdcall IZ::Release(); // for IZ instances

private:
  // Each interface has own counter (for convenience of debugging)
  ULONG ix_counter;
  ULONG iy_counter;
  ULONG iz_counter;
};

CA::CA() :
ix_counter(0),
iy_counter(0),
iz_counter(0){
  std::cout << "CA:CA()" << std::endl;
}

CA::~CA(){
  std::cout << "CA:~CA()" << std::endl;
}


// TODO: this is wrong syntax (function signature)
ULONG __stdcall CA::IX::Release(){
  InterlockedDecrement(&ix_counter);
  ULONG count = ix_counter + iy_counter + iz_counter;
  if (0 == count) delete this;
  return count;
}

void __stdcall CA::Fx(){ std::cout << "\tFx()" << std::endl; }

void __stdcall CA::Fy(){ std::cout << "\tFy()" << std::endl; }

void __stdcall CA::Fz(){ std::cout << "\tFz()" << std::endl; }

// This is function is the same for each interface
HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv){
  HRESULT h = S_OK;
  if (IID_IX == iid){
    *ppv = static_cast<IX*>(this);
    std::cout << "QueryInterface: IX interface returned." << std::endl;
  }
  else if (IID_IY == iid){
    *ppv = static_cast<IY*>(this);
    std::cout << "QueryInterface: IY interface returned." << std::endl;
  }
  else if (IID_IZ == iid){
    *ppv = static_cast<IZ*>(this);
    std::cout << "QueryInterface: IZ interface returned." << std::endl;
  }
  else{
    std::cout << "QueryInterface: Unexpected interface. NULL returned."
      << std::endl;
    h = E_NOINTERFACE;
    *ppv = NULL;
  }
  return h;
}

// TODO: this is wrong syntax (function signature)
ULONG __stdcall CA::IX::AddRef(){
  return InterlockedIncrement(&ix_counter);
}


Спасибо
MasterZiv
Дата: 20.05.2015 13:22:20
Compositum

Интерфейс IUnknown определяет три функции: QueryInterface, AddRef и Release. Мне нужно, чтобы реализация функции QueryInterface была общей для всех трёх интерфейсов (это успешно работает), а вот функции AddRef и Release должны быть определены индивидуально для каждого интерфейса (с этим как раз таки проблема).


Тут мудрить особо не нужно, используй делегирование. Паттерн Proxy кажись.
Т.е. грубо говоря, определи отдельно реализацию, сделай там весь функционал, и вызывай где нужно её, а где не нужно -- не вызывай.

Но вообще достатоно станно, если у тебя есть отдельный AddRef/Release, то это -- отдельный объект, потому как это -- аналоги выделения и освобождения памяти, аналоги создания и удаления объектов. И если у двух разных объектов один QueryInterface -- это как-то странно.

В любом случае, лучше это делать структурно, а не использовать фичи C++.
MasterZiv
Дата: 20.05.2015 13:26:13
Compositum, Ещё раз:

автор
Интерфейс IUnknown определяет три функции: QueryInterface, AddRef и Release. Мне нужно, чтобы реализация функции QueryInterface была общей для всех трёх интерфейсов (это успешно работает), а вот функции AddRef и Release должны быть определены индивидуально для каждого интерфейса (с этим как раз таки проблема).


И код:

class CA :
  public IX,
  public IY,
  public IZ
{
// ...
};


Это очень странно, поскольку ты хочешь (видимо) создавать и удалять части одного объекта независимо. Как у тебя при этом сам объект будет функционировать, если у него нет одной части ? Не очень понятно. Впрочем, это только замечания, решать тебе.
Compositum
Дата: 20.05.2015 13:26:29
MasterZiv,

Я не понял ответа. Специалисты по COM советуют вести отдельный счётчик ссылок для каждого интерфейса, дабы проще было искать ошибки в коде. Декрементирование/инкрементирование ссылок в COM осуществляется посредством AddRef/Release. В книжке пример ведения общего счётчика. Я пытаюсь понять, как реализовать работу со счётчиком отдельно под каждый интерфейс. Буду признателен за пример кода.
Compositum
Дата: 20.05.2015 13:29:40
MasterZiv
Это очень странно, поскольку ты хочешь (видимо) создавать и удалять части одного объекта независимо. Как у тебя при этом сам объект будет функционировать, если у него нет одной части ? Не очень понятно. Впрочем, это только замечания, решать тебе.

Я не собираюсь удалять части одного и того же объекта. Доступ к компоненту в COM может быть представлен одновременно произвольным количеством ссылок через различные интерфейсы. Необходимо отслеживать количество "живых" ссылок, дабы понимать, когда компонент должен быть удалён (компонент сам себя удаляет в этом случае). Это несложно сделать для общего счётчика, но рекомендуется делать отдельный счётчик для каждого интерфейса. Вот это я и пытаюсь реализовать.
MasterZiv
Дата: 20.05.2015 13:33:00
Compositum,

Ну я всё, что мог, сказал.
Dimitry Sibiryakov
Дата: 20.05.2015 14:17:26

Compositum
Как это сделать правильно?

Правильно - никак. Как обычно -
http://stackoverflow.com/questions/2004820/inherit-interfaces-which-share-a-method-name

Posted via ActualForum NNTP Server 1.5

Владимир2012
Дата: 21.05.2015 06:09:19
Оформление класса в виде COM объекта [url=]http://club.shelek.ru/viewart.php?id=48[/url]
Compositum
Дата: 21.05.2015 10:44:42
Владимир2012
Оформление класса в виде COM объекта [url=]http://club.shelek.ru/viewart.php?id=48[/url]

Спасибо, конечно за ссылку, но я не спрашивал о том, как классы оформлять в виде COM. :) По ссылке: материал, на мой взгляд, изложен сумбурно и не понятно. Я изучаю COM по книге Дейла Роджерсона "Основы COM". В ней материал изложен последовательно и неплохо "разжёван".
Compositum
Дата: 21.05.2015 10:45:40
Всем спасибо, устраивающий меня ответ получен здесь.