Использование виртуального / абстрактного класса для определения общего API для конвейерной архитектуры - PullRequest
0 голосов
/ 29 мая 2019

Я хотел бы создать конвейерную архитектуру, состоящую из плагинов, которые принимают различные типы данных и могут создавать различные типы данных, которые затем будут передаваться любому подключенному к нему плагину.Поскольку шаблонные абстрактные функции - это не вещь, я подумал, что когда-либо используемый базовый класс должен был бы определять функции отправки и получения для всех возможных типов.Затем дочерние классы будут определять функции приема для интересующих их типов данных, обрабатывать содержимое, а затем отправлять вновь сгенерированные данные в вектор базовых классов через их функции приема.По умолчанию базовый класс просто возвращает типы данных, для которых он не специализировал функцию приема, поэтому ничего не предпринимает (я понимаю, что здесь, вероятно, есть ненужные накладные расходы).

Мне не удалось вспомнить, что вызов виртуальной функции базы вызовет версию виртуальной функции указанной базы, если только она не определена как чисто виртуальная или объект, который я фактически обрабатываю, принадлежит дочернему объекту.Но поскольку подключенные плагины будут храниться в векторе базовых плагинов, все, к чему у меня был бы доступ, - это функция приема базы.Превращение базового метода приема в чисто виртуальный метод повысило бы вызов дочернего метода получения, но это означало бы, что мне нужно реализовать весь возможный интерфейс для каждого плагина.Есть ли более простой способ сделать это?

В целом, это хороший подход к тому, что я пытаюсь сделать?Этот конвейер плагинов в идеале должен быть динамичным и создаваться по требованию, поэтому соединение плагинов таким образом, казалось бы, является правильным способом.И это должно быть быстро.Если итерация по подключенным плагинам для передачи данных, даже если некоторые плагины ничего не делают с данными, идет медленно, я могу кэшировать данные перед тем, как нажать ссылку, поэтому я перебираю плагины только один раз.

Угадайте этоВсе сводится к тому, существует ли архитектура проектирования, обеспечивающая удобную связь между классами, которая поддерживает различное количество переносимых типов данных.

#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);
};

Ожидаемый: отправленный маршрут 1 int = 10 Отправленный маршрут 2 строка =привет

Ничего не показано для числа с плавающей запятой 3.1415, потому что PrintClass никогда не реализовывал получение для числа с плавающей запятой.

...