Search code examples
c++inheritanceoptimizationtagsdispatch

tag dispatching for inherited classes


I have some code where I have a base class (lets call it foo) that has a variable number of derived classes (between 10-500) created by a generation script. Currently we have a function that will create a new base class by passing in its name as a string and then using a giant if/else statement to find the right one. for example

if      (name == "P2_26") {add_module(new P2_26());}  
else if (name == "P4_30") {add_module(new P4_30());}
...

This leads to a giant if else block. This seems to me like code that could be simplified by using tag dispatching, but every example I find online uses built-ins like iterators that already have tags defined and I could not interpolate to my use case. Is there anyway to streamline this code?


Solution

  • Tag dispatched is based on type information as an input. Judging from your code you have a string as an input which which can not be used in run time.
    Your case looks more like an abstract factory:

    // Factory.h
    class Base;
    
    struct Factory {
      using spawn_t = std::function<Base*()>;
      using container_t = std::unordered_map<std::string, spawn_t>;
      
      static container_t& producers() {
        // This way it will be initialized before first use
        static container_t producers;
        return producers;
      }
    
      static Base* spawn(const std::string& name) {
        auto it = producers().find(name);
        if (it == producers().end()) return nullptr;
        return it->second();
      }
    };
    
    // Base.h
    #define STR(x) #x
    #define DEFINE_REGISTRATOR(_class_) \
    DerivedRegistrator<_class_> _class_::_sRegistrator_(STR(_class_))
    
    #define DECLARE_REGISTRATOR(_class_) \
    static DerivedRegistrator<_class_> _sRegistrator_
    
    template<typename T>
    struct DerivedRegistrator{
      DerivedRegistrator(const std::string& name) {
        Factory::producers()[name] = [](){ return new T(); };
      }
    };
    
    class Base {
      // ...
    };
    

    And then generated files should include:

    // Derived1.h
    class Derived1 : public Base {
      DECLARE_REGISTRATOR(Derived1);
      // ...
    };
    
    // Derived1.cpp
    DEFINE_REGISTRATOR(Derived1); // Will register automatically
    

    This solution will register all classes automatically on program start which is more like what you had before.


    UPD.

    To use it you can simply replace all your if-else code with this line:

    add_module(Factory::spawn(name));
    

    Or if you can't handle nullptr in add_module:

    Base* ptr = Factory::spawn(name);
    if (ptr) {
      add_module(ptr);
    }
    

    Thanks to D Drmmr for making this code better.