Home > database >  How to process every inner std::list?
How to process every inner std::list?

Time:03-17

I have a list: std::list<MyType*>*

MyType is an abstract class and I want to process every list item which is meets the condition, but the list can contains elements which are also type of std::list and I want to process them as well. It can has multiple depths.

I prefer to process it in iterative way, but recursive solutions is also good. Unfortunately I do not know how should I do it yet.

For example a list looks like this:

Root std::list elements:
    Type_A -> int
    Type_B -> std::list
        Type_C -> int
        Type_B -> std::list
            Type_A -> int
            Type_C -> int
        Type_A -> int
        Type_A -> int
        Type_D -> std::list
            Type_A -> int
            Type_A -> int
        Type_A -> int

As you can see Type_A is a scalar type, no problem. Type_B is also a list, I have to process Type_B elements. Type_B also can have std::list items, I have to process them as well, etc.

Code Example:

void MyFunc(std::list<MyType*>* &statements)
{
    std::list<MyType*>::iterator it;
    for (it = statements->begin(); it != statements->end();   it)
    {
        if ((*it)->GetType() == _expectedType) // this type also has a list
        {
            std::list<MyType*>* statementList = dynamic_cast<ExpectedType*>(*it)->GetStatementList();
            std::list<Statement*>::iterator it2 = statementList->begin();
            // do some iterative or recursive process
    }
}

CodePudding user response:

I guess you could use polymorphism and let each node in your tree process itself, something like this:

class MyType
{
  virtual void visit(Environment& env) const = 0;
};

class MyListType : public MyType
{
  std::list<std::unique_ptr<MyType>> _elements;
  void visit(Environment& env) const  override
  {
    for (const auto& node : _elements)
      node->visit(env);
  }
};

class MyLeafType : public MyType
{
  void visit(Environment& env) const  override
  {
    // whatever
  }
};

CodePudding user response:

You can use the visitor pattern

class Type_A;
class Type_B;
class Type_C;
class Type_D;

struct MyType_Visitor {
    virtual void visit(Type_A & a) = 0;
    virtual void visit(Type_B & b) = 0;
    virtual void visit(Type_C & c) = 0;
    virtual void visit(Type_D & d) = 0;
};

class MyType {
public:
    virtual ~MyType() = default;
    virtual void accept(MyType_Visitor & visitor) = 0;
    // existing members
}

class Type_A : public MyType {
public:
    void accept(MyType_Visitor & visitor) override {
        visitor.visit(*this); // calls void visit(Type_A & a)
    }
    // existing members
};

class Type_B : public MyType {
    std::list<MyType *> children;
public:
    void accept(MyType_Visitor & visitor) override {
        visitor.visit(*this); // calls void visit(Type_B & b)
        for (MyType * child : children) {
            child->accept(visitor); // also visit all the children
        }
    }
    // existing members
};

class Type_C : public MyType {
public:
    void accept(MyType_Visitor & visitor) override {
        visitor.visit(*this); // calls void visit(Type_C & c)
    }
    // existing members
};

class Type_D : public MyType {
    std::list<MyType *> other_things;
public:
    void accept(MyType_Visitor & visitor) override {
        visitor.visit(*this); // calls void visit(Type_D & d)
        for (MyType * thing : other_things) {
            thing->accept(visitor); // also visit all the children
        }
    }
    // existing members
};

This then allows you to define different behaviours for different situations, e.g.

class PrintVisitor : public MyType_Visitor {
    void visit(Type_A & a) override {
        std::cout << "Type_A -> int" << std::endl;
    }
    void visit(Type_B & b) override {
        std::cout << "Type_B -> std::list" << std::endl;
    }
    void visit(Type_C & c) override {
        std::cout << "Type_C -> int" << std::endl;
    }
    void visit(Type_D & d) override {
        std::cout << "Type_D -> std::list" << std::endl;
    }
}

class MeetsConditionVisitor : public MyType_Visitor {
    void visit(Type_A &) override { /* only care about B */ }
    void visit(Type_B & b) override {
        if (/* b specific something */) {
           // stuff
        }
    }
    void visit(Type_C &) override { /* only care about B */ }
    void visit(Type_D &) override { /* only care about B */ }
}

CodePudding user response:

If potentially any of the subclasses can have children, then you could have a virtual member function to expose that.

class MyType {
public:
    virtual ~MyType() = default;
    virtual const std::list<MyType*> & children() {
        // default -> no children
        static const std::list<MyType*> empty;
        return empty;
    }
    // existing members
}

class Type_B : public MyType {
    std::list<MyType*> stuff;
public:
    const std::list<MyType*> & children() override { return stuff; }
}

class Type_D : public MyType {
    std::list<MyType*> things;
public:
    const std::list<MyType*> & children() override { return things; }
}

void MyFunc(const std::list<MyType*> & statements)
{
    for (auto * item : statements)
    {
        // stuff before visiting children
        MyFunc(item->children());
        // other stuff after visiting children
    }
}
  • Related