How to automatically register a class on creation

前端 未结 6 1619
没有蜡笔的小新
没有蜡笔的小新 2020-11-29 03:50

I was wondering whether a design pattern or idiom exists to automatically register a class type. Or simpler, can I force a method to get called on a class by simply extendin

相关标签:
6条回答
  • 2020-11-29 04:08

    @AMCoder: This is not the answer you want. The answer you want, reflection (e.g., what_am_I()) doesn't really exist in C++.

    C++ has in a rather limited form via the RTTI. Using RTTI in the base class to determine the "true" type of the object being constructed won't work. Calling typeid(*this) in a base class will instead give you the typeinfo for the class being constructed.

    You can make your class Animal have a non-default constructor only. This works fine for classes that derive directly from Animal, but what about classes that derive from a derived class? This approach either precludes further inheritance or requires class builders to create multiple constructors, one of which is for use by derived classes.

    You can use Luchian's CRTP solution, but this too has problems with inheritance, and it also precludes you from having a collection of pointers to Animal objects. Add that non-template base class back into the mix so you can have a collection of Animals and you have the original problem all over again. Your documentation will have to say to only use the template to make a class that derives from Animal. What happens if someone doesn't do that?

    The easiest solution is the one you don't like: Require that every constructor of a class that derives from Animal must call register_animal(). Say so in your documentation. Show the users of your code some examples. Put a comment in front of that example code, // Every constructor must call register_animal(). People who use your code are going to use cut and paste anyhow, so have some cut-and-paste ready solutions on hand.

    Worrying about what happens if people don't read your documentation is a case of premature optimization. Requiring that every class call register_animal() in their constructors is a simple requirement. Everyone can understand it and everyone can easily implement it. You've got much bigger troubles on your hands with your users than a failed registration if your users can't even follow this simple instruction.

    0 讨论(0)
  • 2020-11-29 04:09

    Usually I do this with a macro.

    Unit test frameworks often employ the technique for registering the tests, e.g. GoogleTest

    0 讨论(0)
  • 2020-11-29 04:11

    You can indeed do this using the curiously recursive template idiom. It requires nothing from whoever is extending the class that can't be enforced by the compiler:

    template<class T>
    struct Animal
    {
       Animal()
       { 
          reg;  //force specialization
       }
       virtual std::string name() = 0;
       static bool reg;
       static bool init() 
       { 
          T t; 
          AnimalManager::registerAnimal(t.name());
          return true;
       }
    };
    
    template<class T>
    bool Animal<T>::reg = Animal<T>::init();
    
    struct Tiger : Animal<Tiger>
    {
       virtual std::string name() { return "Tiger"; }
    };
    

    In this code, you can only extend Animal if you specialize it. The constructor forces the static member reg to be initialized, which in turn calls the register method.

    EDIT: As pointed out by @David Hammen in the comments, you won't be able to have a collection of Animal objects. However, this can easily be solved by having a non-template class from which the template inherits and use that as a base class, and only use the template for extending.

    0 讨论(0)
  • 2020-11-29 04:13

    If you insist every animal should be registered, why not just make name a parameter of Animal constructor. Then you can put register issues to Animal constructor and every derived will have to pass valid name and register:

    struct Animal
    {
       Animal(std::string name){ AnimalManager::registerAnimal(name);}
    }
    
    struct Tiger : Animal
    {
       Tiger():Animal("Tiger"){}
    };
    
    0 讨论(0)
  • 2020-11-29 04:22

    This is a typical example where you want to do some sort of bookkeeping when an object is constructed. Item 9 in Scott Meyers "Effective C++" gives an example of this.

    Basically you move all the bookkeeping stuff to base class. Derived class explicitly constructs base class and passes the information that is required for Base class construction. For example:

    struct Animal
    {
      Animal(std::string animal_type)
      {
        AnimalManager::registerAnimal(animal_type);
      };
    };
    
    struct Dog : public Animal
    {
      Dog() : Animal(animal_type()) {};
    
    
      private:      
        static std::string animal_type()
        {
          return "Dog";
        };
    };
    
    0 讨论(0)
  • 2020-11-29 04:29

    You could just call a method in the base class constructor, which will get called everytime a derived class gets instantiated, as follows:

    class Animal {
    public:
        Animal() {doStuff();}
    }
    

    The doStuff() method could be implemented in the base class to do static operations, or it could be pure virtual and be implemented in derived classes.

    Edit: As correctly pointed out in the comments, virtual methods cant be called in the ctor.

    Notice though that the base class constructor will be called before the derived constructors, so you could also do something like this:

    class Animal {
    public:
        Animal(const std::string &name) {doStuff(name);}
    
    private:
        Animal(); // Now nobody can call it, no need to implement
    }
    
    class Dog : public Animal {
        Dog() : Animal("Dog") {}
    }
    

    Hope that helps

    0 讨论(0)
提交回复
热议问题