интерфейсы для шаблонных классов - PullRequest
1 голос
/ 10 августа 2009

Я работаю над структурой плагинов, которая поддерживает несколько вариантов базового класса плагинов CPlugin : IPlugin. Я использую boost::shared_ptr<IPlugin> для всех ссылок на плагины, за исключением случаев, когда подсистеме нужен специфический интерфейс типа плагина. Мне также нужна возможность клонировать плагин в другой отдельный объект. Это должно вернуть PluginPtr. Вот почему CPlugin является шаблоном, а не прямым классом. CPlugin::Clone() - то, где используется параметр шаблона. Ниже приведены определения классов, которые я использую:

IPlugin.h

#include "PluginMgr.h"
class IPlugin;
typedef boost::shared_ptr<IPlugin> PluginPtr;

class IPlugin
{
public:
  virtual PluginPtr Clone() =0;
  virtual TYPE Type() const =0;
  virtual CStdString Uuid() const =0;
  virtual CStdString Parent() const =0;
  virtual CStdString Name() const =0;
  virtual bool Disabled() const =0;

private:
  friend class CPluginMgr;
  virtual void Enable() =0;
  virtual void Disable() =0;
};

CPlugin.h

#include "IPlugin.h"
template<typename Derived>
class CPlugin : public IPlugin
{
public:
  CPlugin(const PluginProps &props);
  CPlugin(const CPlugin&);
  virtual ~CPlugin();
  PluginPtr Clone();

  TYPE Type() const { return m_type; }
  CStdString Uuid() const { return m_uuid; }
  CStdString Parent() const { return m_guid_parent; }
  CStdString Name() const { return m_strName; }
  bool Disabled() const { return m_disabled; }

private:
  void Enable() { m_disabled = false; }
  void Disable() { m_disabled = true; }

  TYPE        m_type;
  CStdString  m_uuid;
  CStdString  m_uuid_parent;
  bool        m_disabled;
};

template<typename Derived>
PluginPtr CPlugin<Derived>::Clone()
{
  PluginPtr plugin(new Derived(dynamic_cast<Derived&>(*this)));
  return plugin;
}

Пример конкретного класса CAudioDSP.h

#include "Plugin.h"
class CAudioDSP : CPlugin<CAudioDSP>
{
  CAudioDSP(const PluginProps &props);
  bool DoSomethingTypeSpecific();
  <..snip..>
};

Моя проблема (наконец-то) заключается в том, что CPluginMgr необходимо обновить m_disabled конкретного класса, однако, когда он передан PluginPtr, он не может определить тип и вести себя по-разному в соответствии с параметром шаблона. Я не могу понять, как избежать объявления ::Enable() и ::Disable() закрытыми членами IPlugin, но это мгновенно означает, что каждый раздел приложения теперь должен знать о классе CPluginMgr, поскольку он объявлен как друг в шапке. Появляется круговая зависимость ада. Я вижу другой вариант, объявляю функции включения / выключения частными членами CPlugin и использую boost::dynamic_pointer_cast<CVariantName>.

void CPluginMgr::EnablePlugin(PluginPtr plugin)
{
  if(plugin->Type == PLUGIN_DSPAUDIO)
  {
    boost::shared_ptr<CAudioDSP> dsp = boost::dynamic_pointer_cast<CAudioDSP>(plugin);
    dsp->Enable();
  }
}

Это, однако, приводит к большому количеству дублирующегося кода с множеством вариантов базового шаблона CPlugin. Если у кого-то есть лучшее предложение, пожалуйста, поделитесь им!

Ответы [ 2 ]

2 голосов
/ 10 августа 2009

Вы можете легко написать:

class CPluginMgr;

class IPlugIn ..
{
    friend CPluginMgr;
    ...
};

Для друга требуется только предопределение.

0 голосов
/ 10 августа 2009

Я думаю, у вас проблемы с попыткой вернуть shared_ptr в методе клонирования. Почему бы вам не использовать ковариантные типы возврата ? То, что вы делаете, это общая идиома под названием Виртуальный конструктор .

class IPlugin
{
public:
    virtual IPlugin* clone() = 0;
    // ...
}

class CPluginMgr;

class CPlugin : public IPlugin
{
public:
    virtual CPlugin* clone() = 0;
    friend CPluginMgr; // as @Christopher pointed out
    void Enable(bool enable) { m_disabled = !enable; }
    // ...
}

class CAudioDSP : public CPlugin 
{
public:
    virtual CAudioDSP* clone();
    // ...
}

CAudioDSP* CAudioDSP::clone()
{
    return new CAudioDSP(*this); // assume copy constructors are properly implemented
}

Возвращение shared_ptr может привести к ошибкам (раннему уничтожению временных объектов), и я думаю, что это обычно не очень хорошая идея.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...