首页 > 解决方案 > 使用虚拟/抽象类为管道架构定义通用 API

问题描述

我想创建一个由插件构成的管道架构,该架构可以摄取各种数据类型并可以生成各种数据类型,然后将这些数据类型提供给与之连接的任何插件。由于模板化的抽象函数不是一个东西,我认为我使用的任何基类都需要为所有可能的类型定义发送和接收函数。然后,子类将为他们感兴趣的数据类型定义接收函数,处理内容,然后通过它们的接收函数将新生成的数据发送到基类的向量。默认情况下,基类只会返回它没有专门针对接收函数的数据类型,因此不做任何事情(我知道这里可能有不必要的开销)。

我不记得调用基的虚函数将调用所述基的虚函数版本,除非定义为纯虚拟或我实际处理的对象是子对象。但是由于连接的插件将存储在基本插件的向量中,所以我只能访问基本插件的接收功能。将基础的接收方法转换为纯虚拟方法会提升对子接收方法的调用,但这意味着我需要为每个插件实现整个可能的接口。有没有更简单的方法来做到这一点?

更一般地说,这是我想要做的一个好方法吗?理想情况下,这个插件管道应该是动态的并按需创建,因此以这种方式将插件连接在一起似乎是正确的方法。它需要快速。如果迭代连接的插件以推送数据,即使某些插件对数据不做任何事情也很慢,我可以在推送引用之前缓存数据,因此我只迭代插件一次。

猜猜这归结为,是否有一种设计架构允许在支持不同数量的可传输数据类型的类之间进行方便的通信。

#define ADD_TYPE(type) \
    inline void send(const routing::route_t route, const type& data) { for(auto &plugin : m_registered_plugins) plugin->receive(route, data); } \
    virtual inline void receive(const routing::route_t& route, const type& data) { return; }
        // Thought about trying this second -->
        // virtual inline void receive(const routing::route_t& route, const type& data) = 0; 

class PluginBase
{
public:

    PluginBase(const std::string& name) 
        : m_uuid(m_uuid_gen())
        , m_log(name)
    { }

    virtual ~PluginBase() { }

    bool pluginIsDescendant(PluginBase* plugin) const
    {
        for (auto registered : m_registered_plugins)
        {
            // Did we find the plugin
            if (registered == plugin)
                return true;

            // Is the plugin a descendant of this
            if (registered->pluginIsDescendant(plugin))
                return true;
        }

        return false;
    }

    bool connect(PluginBase* plugin)
    {
        // Don't connect to self
        if (plugin == this)
        {
            m_log.error("Cannot connect plugin to self!");
            return false;
        }

        // Check for recursion
        if (plugin->pluginIsDescendant(this))
        {
            m_log.error("Cannot connect! Plugin recursion detected.");
            return false;
        }

        // Check if it already exists in the forward pipeline
        if (pluginIsDescendant(plugin))
            m_log.warning("Plugin already connected as descendant.");

        m_registered_plugins.push_back(plugin);

        return true;
    }

    ADD_TYPE(int);
    ADD_TYPE(std::string);
    ADD_TYPE(float);

protected:

    // Logger
    logger::Log m_log;

private:

    // Static boost generator
    static boost::uuids::random_generator m_uuid_gen;

    // UUID of plugin
    boost::uuids::uuid m_uuid;

    // Vector of registered analytics
    std::vector<PluginBase*> m_registered_plugins;
};



// EXAMPLE number CHILD CLASS
class NumberClass: public PluginBase
{
public:
  void receive(const routing::route_t& route, const int value) 
  {
     int output= transform(route, value);
     send(route, output);
  } 

  void receive(const routing::route_t& route, const float value) 
  {
     float output= transform(route, value);
     send(route, output);
  } 
};

// EXAMPLE std::string CHILD CLASS
class StringClass : public PluginBase
{
public:
  void receive(const routing::route_t& route, const std::string value) 
  {
     std::string output= transform(route, value);
     send(route, output);
  } 
};

// EXAMPLE print CHILD CLASS
class PrintClass : public PluginBase
{
public:
  void receive(const routing::route_t& route, const int value) 
  {
     std::cout << "Route " << route << " sent int = " << value << std::endl;
  } 

  void receive(const routing::route_t& route, const std::string value) 
  {
     std::cout << "Route " << route << " sent string = " << value << std::endl;
  } 
};

int main()
{
  NumberClass c1;
  StringClass c2;
  NumberClass c3;
  PrintClass c4;
  c1.connect(c4);
  c2.connect(c4);
  c3.connect(c4);

  c1.receive(1, 10);
  c2.receive(2, "hello");
  c3.receive(3, 3.1415);
};

预期:Route 1 sent int = 10 Route 2 sent string = hello

浮点数 3.1415 没有显示任何内容,因为 PrintClass 从未实现浮点数的接收。

标签: c++pluginsabstract-classpipeline

解决方案


推荐阅读