c++design-patternsembeddeddynamic-typingstatic-allocation

embedded c++ : dynamic typing without dynamic allocation?


Here's my issue, simplified:

    enum AnimalType_t
    {
                DOG                         = 0,
                GREY_HOUND                  = 1,
                IMMORTAL_JELLYFISH          = 2,
    };
    struct RawData_t
    {
        int             age;
        AnimalType_t    typeOfAnimal;
    };

    RawData_t GetMyCurrentRawData();//returns the current raw data
    bool      IsDataReady(); //returns true if data is ready, false otherwise
    class Animal
    {
    public:
        virtual Animal();
        virtual ~Animal();
        int GetType() { return rawAttributes.typeOfAnimal; };   //the only implementation for all children
        virtual int GetAge() { return rawAttributes.age; };     //to be implemented in the child class
        virtual void UpdateAge() { rawAttributes.age++; };  //to be implemented in the child class
        virtual int GetNumberOfLegs() = 0;                      //to be implemented in the child class
    private:
        RawData_t rawAttributes;
    }
    class Dog : public Animal
    {
    public:
        Dog(RawData rawData):Animal(rawData){};
        int GetNumberOfLegs() {return 4;};                  
    };

    class GreyHound : public Dog
    {
    public:
        GreyHound(RawData rawData):Dog(rawData){};
    };

    class ImmortalJellyFish : public Animal
    {
    public:
        ImmortalJellyFish(RawData rawData):Animal(rawData){};
        int GetNumberOfLegs() {return 0;};      
        void UpdateAge() { return;} override;
    };
    class Building
    {
    public:
        Building(){};
        //sorry for the long line, but you get the idea...
        int Display(void){if(IsDataReady()) DisplayOnScreen("This animal ( "+ animal_m.GetType()+") has " + animal_m.GetNumberOfLegs() + "legs and is " + animal_m.GetAge() + " years old\n";};
        int Live(void){currentDiagCode_m.UpdateAge();};

    private:
        auto                        animal_m; //?? not working
    };

    static Building paddock;
    static Building farm;

    void Buildings_Step(void)
    {
        paddock.Live();
        paddock.Display();
        farm.Live();
        farm.Display();
    }

Here's where i'm struggling:

Here're my constraints:

I though about :

Is there any design/model that could fulfill my needs?

Thank you!


Solution

  • In C++, memory allocation and object existence are two separate concepts, even though in most situations you'll handle both together. In your case, though, you may wish to explicitly separate the two:

    1. Create enough memory for any object:

      char buf[N];    // N >= sizeof(T) for all T in your hierarchy
      
    2. To create an animal:

      new (buf) GreyHound(args);
      
    3. To destroy an existing animal (and make room for another):

      reinterpret_cast<Animal*>(buf)->~Animal();
      

    That is, you obtain storage as part of your container object, but you manage the Animal object lifetime dynamically with placement-new and explicit destruction.

    There's a bit more to this: your memory also needs to be aligned correctly for all types that you construct in it. You can use some library helper traits like std::aligned_storage or std::aligned_union to simplify the computation, though you'll probably still need to do a bit of work to compute both the size and the alignment.


    As a completely separate alternative, you could forego the polymorphic class hierarchy and use a std::variant instead. This is conceptually similar, but a somewhat different approach implementation-wise. The reason that this is conceptually similar is because you have a bounded set of types, so you don't really need polymorphism to handle arbitrary, unknown derived types at runtime.