存储多个插件(不同类的对象)并相互访问

时间:2012-05-27 14:03:53

标签: c++ qt dll

我有几个从基类File_plugin推迟的类。我希望在类File的每个实例中都有一个每个类的实例。 这是一个天真的实现:

class File {
public:
  File(): plugin1(this), plugin2(this), /*... */ {}
  Plugin1 plugin1;
  Plugin2 plugin2;
  Plugin3 plugin3;
  Plugin4 plugin4;
}; 

class File_plugin {
public:
  File_plugin(File* p_file): file(p_file) {}

protected:
  File* file;
};

class Plugin1: public File_plugin {
  void some_action() {
    //we can access another plugin as:
    Plugin2* p2 = &( file->plugin2 );
  }
};

但是File类必须知道所有插件的实现,这很糟糕。我们通常在每个地方尽可能使用前向声明。 因此,File一定不能知道插件的实现,甚至不知道类名列表。一些插件将在运行时从DLL加载,因此我们在编译时不知道它们的名称。

此外,插件必须不知道几乎所有其他插件的存在。但是如果一个插件知道其他插件,它必须能够获得这个插件的对象并使用它。

现在我正在使用以下实现:

class File {
public:
  File() : plugins(Plugins_registry::get_file_plugins(this)) { }

  template<class T>
  T *get_plugin() {
    foreach(File_plugin *p, plugins) {
      T* o = dynamic_cast<T*>(p);
      if (o) return o;
    }
    throw Bad_exception(tr("File plugin not found"));
  }
private:
  QList<File_plugin*> plugins;
}

QList<File_plugin*> Plugins_registry::get_file_plugins(File* file) {
  QList<File_plugin*> list;
  list << new Plugin1(file);
  list << new Plugin2(file);
  list << new Plugin3(file);
  list << new Plugin4(file);

  //code below is untested
  QPluginLoader loader("plugin5.dll");
  list << dynamic_cast<My_plugin_class>(loader.instance)->get_file_plugin();

  return list;
}

class Plugin1: public File_plugin {
  void some_action() {
    //we can access another plugin as:
    Plugin2* p2 = file->get_plugin<Plugin2>();
  }
};

某些插件可以存储在外部库中(在Windows上表示为DLL)。它现在没有实现,但将来会实现。我认为我目前的实施允许,但我没有检查。

此实现的最大问题是插件搜索速度慢(使用fordynamic_cast)。 所以我不能只在任何地方调用get_plugin,我必须存储返回的值并将其用于快速访问。这很不舒服。 此外,这个解决方案看起来并不完美。有没有办法让它变得更好?

1 个答案:

答案 0 :(得分:1)

以下是如何通过为每个插件类型创建地图来实现它的示例:

class File {
public:
  File() { }

  template<class T>
  T *get_plugin() {
    T *result = T::getInstance(this);
    if (!result) {
      throw Bad_exception(tr("File plugin not found"));
    }
    return result;
  }
};

class File_plugin {
  public:
    File_plugin(File *file)
    : file(file)
    {
    }

  protected:
    File *file;
};

template <class Plugin>
class Basic_File_Plugin : public File_plugin {
  public:
    Basic_File_Plugin(File *file,Plugin *plugin)
    : File_plugin(file)
    {
      instances[file] = plugin;
    }

    ~Basic_File_Plugin()
    {
      instances.erase(file);
    }

    static Plugin *getInstance(File *file)
    {
      return instances[file];
    }

  private:
    static std::map<File *,Plugin *> instances;
};


template <class Plugin>
std::map<File *,Plugin *> Basic_File_Plugin<Plugin>::instances =
  std::map<File *,Plugin *>();

class Plugin1 : public Basic_File_Plugin<Plugin1> {
  public:
    Plugin1(File *file)
    : Basic_File_Plugin<Plugin1>(file,this)
    {
    }

    void some_action();
};

class Plugin2 : public Basic_File_Plugin<Plugin2> {
  public:
    Plugin2(File *file)
    : Basic_File_Plugin<Plugin2>(file,this)
    {
    }
};


void Plugin1::some_action()
{
  Plugin2 *p2 = file->get_plugin<Plugin2>();
}