title: Using an virtual/abstract class to define an common API for a pipeline architecture
body: <p>I'd like to create a pipeline architecture constructed of plugins that ingest a variety of data types and can produce a variety of data types that would then be fed to any plugin connected to it. Since templated abstract functions aren't a thing, I figured what ever base class I used would need to define send and receive functions for all possible types. Child classes would then define receive functions for data types they are interested in, process the content, then send the newly generated data on to a vector of base classes via their receive functions. By default, the base class would just return on data types it hasn't specialized a receive function for, thus not doing anything (I understand there is probably unnecessary overhead here). </p>
  
  <p>I failed to recall that calling a base's virtual function will invoke said base's version of the virtual function unless defined as pure virtual or the object I'm actually handling was that of the child. But since connected plugins would be stored in a vector of base plugins, all I would have had access to is the base's receive function. Turning the base's receive method into a pure virtual method would elevate the call to the child's receive method but that would mean I need to implement the entire possible interface for each plugin. Is there an easier way to doing this? </p>
  
  <p>More general, is this a good approach to what I'm trying to do? This plugin pipeline would ideally be dynamic and created on demand so connecting plugins together in such a fashion seemed to be the right way to go. And it needs to be quick. If iterating over connected plugins to push data even when some plugins don't do anything with the data is slow, I can cache the data before pushing the reference on so I only iterate through the plugins once.</p>
  
  <p>Guess this boils down to, is there a design architecture out there that allows for convenient communication between classes that supports a varying amount of transferable data types.</p>
  
  <pre><code>#define ADD_TYPE(type) \
      inline void send(const routing::route_t route, const type&amp; data) { for(auto &amp;plugin : m_registered_plugins) plugin-&gt;receive(route, data); } \
      virtual inline void receive(const routing::route_t&amp; route, const type&amp; data) { return; }
          // Thought about trying this second --&gt;
          // virtual inline void receive(const routing::route_t&amp; route, const type&amp; data) = 0; 
  
  class PluginBase
  {
  public:
  
      PluginBase(const std::string&amp; 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-&gt;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-&gt;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&lt;PluginBase*&gt; m_registered_plugins;
  };
  
  
  
  // EXAMPLE number CHILD CLASS
  class NumberClass: public PluginBase
  {
  public:
    void receive(const routing::route_t&amp; route, const int value) 
    {
       int output= transform(route, value);
       send(route, output);
    } 
  
    void receive(const routing::route_t&amp; 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&amp; 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&amp; route, const int value) 
    {
       std::cout &lt;&lt; "Route " &lt;&lt; route &lt;&lt; " sent int = " &lt;&lt; value &lt;&lt; std::endl;
    } 
  
    void receive(const routing::route_t&amp; route, const std::string value) 
    {
       std::cout &lt;&lt; "Route " &lt;&lt; route &lt;&lt; " sent string = " &lt;&lt; value &lt;&lt; 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);
  };
  </code></pre>
  
  <p>Expected: 
  Route 1 sent int = 10
  Route 2 sent string = hello</p>
  
  <p>Nothing is shown for the float 3.1415 because PrintClass never implemented the receive for float.</p>