mercredi 1 mai 2019

How to dynamically choose the return type of the operator [ ] in composite design pattern?

First of all, I want to point out that it is the first time I am using dynamic polymorphism and the composite design pattern.

I would like to use the composite design pattern to create a class Tree which is able to take different objects of the type Tree, a composite type, or Leaf, an atomic type. Both Tree and Leaf inherit from a common class Nature. Tree can store Leaf or Tree objects into a std::vector<std::shared_ptr<Nature>> children. I would like to fill the vector children with a syntax of this kind (so I guess I have to use variadic, to consider a generic number of inputs in the input lists), as in the following:

Leaf l0(0);
Leaf l1(1);
Tree t0;
Tree t1;
t0.add(l0,l1);
t1.add(t0,l0,l1); // or in general t1.add(t_00,...,t_0n, l_00,...,l_0n,t10,...,t1n,l10,...,l1n,.... )

Then I would also access different elements of a Tree by means of the operator[ ]. So for example t1[0] returns t0 and t1[0][0] returns l0, while t1[0][1] returns l0.

Also I would like an homogeneous behaviour. So either use -> or the dot for accessing the methods on all levels (tree or leaf).

Is it possible to achieve this behaviour?

The implementation of such classes can be like the following:

class Nature
{
  public:
    virtual void nature_method() = 0;
    virtual~Nature();
    //virtual Nature& operator[] (int x);

};
class Leaf: public Nature
{
    int value;
  public:
    Leaf(int val)
    {
        value = val;
    }
    void nature_method() override
    {
        std::cout << " Leaf=="<<value<<" ";
    }
}; 
class Tree: public Nature
{ 
    private:
    std::vector <std::shared_ptr< Nature > > children;
    int value;

    public:
    Tree(int val)
    {
        value = val;
    }


     void add(const Nature&);

     void add(const Leaf& c)
    {
        children.push_back(std::make_shared<Leaf>(c));
    } 

     void add(const Tree& c)
    {
        children.push_back(std::make_shared<Tree>(c));
    }   


    void add(std::shared_ptr<Nature> c)
    {
        children.push_back(c);
    }

     template<typename...Args>
    typename std::enable_if<0==sizeof...(Args), void>::type
    add(const Leaf& t,Args...more)
    {
     children.push_back(std::make_shared<Leaf>(t));
    };

    template<typename...Args>
    typename std::enable_if<0==sizeof...(Args), void>::type
    add(const Tree& t,Args...more)
    {
     children.push_back(std::make_shared<Tree>(t));
    };


    template<typename...Args>
    typename std::enable_if<0<sizeof...(Args), void>::type
    add(const Leaf& t,Args...more)
    {
      children.push_back(std::make_shared<Leaf>(t));
      add(more...);
    };

    template<typename...Args>
    typename std::enable_if<0<sizeof...(Args), void>::type
    add(const Tree& t,Args...more)
    {
      children.push_back(std::make_shared<Tree>(t));
      add(more...);
    };

    void nature_method() override
    {
        std::cout << " Tree=="<< value;
        for (int i = 0; i < children.size(); i++)
          children[i]->nature_method();
    }
}

I could implement the overload operator [] to return a pointer to Nature or a Nature object, like so:

 Nature& operator[] (int x) {
        return *children[x];
    }

 std::shared_ptr< Nature > operator[] (int x) {
        return children[x];
    }

In both cases, the return type is Nature related. This because it could be a Leaf or a Tree, which is not known in advance. But since the return type of the operator has to be known at compile time, I cannot do something else.

However, if the returned type would be Tree related, I cannot use the operator [] anymore, because I have enforced it to be Nature.

How can I dynamically choose the return type, Tree or Leaf related, of []? Is there any workaround for this?

I could consider operator [] a virtual method in the Nature class, but still I would no what to make out of this.

I have read about covariant types as well, but I do not know if they would be applicable here.

Thank you.

Aucun commentaire:

Enregistrer un commentaire