lundi 11 janvier 2021

How to manage different types of data in the base class?

I'll throw some code in your eyes first.

// Example program
#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <functional>

class Model
{
    std::vector<std::function<void(int)>> m_callbacks;
    public:
    void addUpdateCallback(std::function<void(int)> p_func) {m_callbacks.push_back(p_func);}
    void valueUpdated(int p_attrId) 
    {
        for(auto callback: m_callbacks)
        {
            callback(p_attrId);
        }
    }
    
    virtual bool set(int p_attrId, int p_value) {return false;}; 
    virtual bool get(int p_attrId, int & p_value) const {return false;};

};

class Derived1: public Model
{
    static constexpr int c_classId = 1;
    int value = 1;
    public:
    
    enum EAttrs
    {
        eAttr1 = c_classId * 1000
    };
    
    virtual bool set(int p_attrId, int p_value) override
    {
        switch(p_attrId)
        {
            case eAttr1:
                value = p_value;
                break;
            
            default:
                return Model::set(p_attrId, p_value);
        }
        valueUpdated(p_attrId);
        return true;
    }

    virtual bool get(int p_attrId, int & p_value) const override
    {
        switch(p_attrId)
        {
            case eAttr1:
                p_value = value;
                return true;
            
            default:
                return Model::get(p_attrId, p_value);
        }
    }
};


class Derived2: public Model
{
    static constexpr int c_classId = 2;
    int value = 2;
    public:
    
    enum EAttrs
    {
        eAttr1 = c_classId * 1000
    };
    
    virtual bool set(int p_attrId, int p_value) override
    {
        switch(p_attrId)
        {
            case eAttr1:
                value = p_value;
                break;
            
            default:
                return Model::set(p_attrId, p_value);
        }
        valueUpdated(p_attrId);
        return true;
    }

    virtual bool get(int p_attrId, int & p_value) const override
    {
        switch(p_attrId)
        {
            case eAttr1:
                p_value = value;
                return true;
            
            default:
                return Model::get(p_attrId, p_value);
        }
    }
};

// GuiTextBoxComponent.h

// no includes to any class derived from model

class GuiTextBoxComponent
{
        std::weak_ptr<Model> m_model;
        int m_attrId;
    public:
        void valueUpdated(int p_attrId)
        {
            auto locked = m_model.lock();
            if(locked)
            {
                int value;
                bool result = locked->get(p_attrId, value);
                if(!result)
                {
                    std::cout << "Failed to get attribute " << p_attrId << "\n";
                    return;
                }
            
                std::cout << "AttrID: " << p_attrId << " Value: " << value << "\n";
            }
            else
            {
                std::cout << "Model is dead\n";
            }
        }
        
        void setSource(std::weak_ptr<Model> p_model, int p_attrId)
        {
            m_model = p_model;
            m_attrId = p_attrId;
        }
};


int main()
{
    std::shared_ptr<Model> model1 (new Derived1);
    std::shared_ptr<Model> model2 (new Derived2);
    
    GuiTextBoxComponent textbox1;
    GuiTextBoxComponent textbox2;
    
    textbox1.setSource(model1, Derived1::eAttr1);
    model1->addUpdateCallback(std::bind(&GuiTextBoxComponent::valueUpdated, &textbox1, std::placeholders::_1));
    textbox2.setSource(model2, Derived1::eAttr1);
    model2->addUpdateCallback(std::bind(&GuiTextBoxComponent::valueUpdated, &textbox2, std::placeholders::_1));
    
    model1->set(Derived1::eAttr1, 42);
    model2->set(Derived2::eAttr1, 69); // nice
}

The motivation behind this is acquisition of all data from a single interface.

The challenge with this design is that the Model interface needs to implement all types required from anywhere in the program.

How would you extend the types provided?

Is there some other design that could be used to achieve similar results?


EDIT

Updated the example code to illustrate a point I was trying to make. I need to be able to add functionality like the GuiTextBoxComponent, without #include "Derived1.h" in its header.

Aucun commentaire:

Enregistrer un commentaire