Baumoperationen



  • Hi,
    ich möchte Ausdrucksbäume irgendwie mal vernünftig umsetzen, finde aber keine zufriedenstellende Lösung für "Umhänge-Operationen". Was ich bislang habe funktioniert eigentlich, aber ist schon recht hässlich. Da ich aber noch viele Ideen habe, was ich mit den Bäumen machen möchte, wäre es toll, ein vernünftiges Fundament zu haben.

    Sagen wir, ich habe folgenden Knotentyp:

    class Node {
       // ...
    
       iterator begin();
       iterator end();
    
       unique_ptr< Impl > impl_;
       vector< Node > children_;
    }
    

    Wie würdet ihr das Umhängen einer Range von Kindern eines Knotens an einen anderen Knoten umsetzen?

    Bislang habe ich mir folgendes überlegt:

    class Node {
      // ...
    
      // 1 node_list enthält Referenz auf Quellenknoten
      //   und die beiden iteratoren. Irgendwie wird es so zusammengehackt,
      //   dass beim Destruktor-Aufruf von node_list die Elemente in der Quelle
      //   gelöscht werden. Aber es besteht irgendwie das Risiko, dass der
      //   client code es doch irgendwie hinbekommt, die node_list am Leben
      //   zu halten.
      node_list extract( const_iterator, const_iterator );
      void insert( const_iterator where, node_list );
    
      // 2 - Auch keine Temporary-Liste, aber ziemlich unschön in der Verwendung, oder nicht?
      void absorb( const_iterator where, Node& source, const_iterator b, const_iterator e );
    
      // ...
    };
    

    Habt ihr andere Lösungen? Wie macht ihr sowas? Wenn parent-Beziehung gespeichert wird, dann kann man das eigentlich nach außen hin ganz schön hinfrickeln (Ohne die zusätzliche Node-Referenz), aber die brauche ich ansonsten eigentlich nicht...

    Auch bin ich noch am überlegen, ob es so eine gute Idee ist, die Kinderliste direkt mit in die Node-Klasse zu nehmen, weil so ja auch der children_-vector des Vaterknotens damit belastet wird... Der könnte zwar Zeiger statt die Knoten selber speichern, aber dann müsste erst ein Zeiger dereferenziert werden um an den Kindknoten zu kommen und dann noch einer um zum Impl zu kommen, die beide ja irgendwo im Speicher liegen können.

    Eine andere Lösung wäre vielleicht:

    class Node {   
       template< typename T, typename... Args >
       void construct_impl( Args&& args ) {
           if( sizeof(T) <= 64 ) {
             ...
           } else {
             ...
           }
       }
    
       unique_ptr<Impl,NodeDeleter> ptr_;
       std::aligned_storage<64> small_node_;
       std::vector< unique_ptr< Node > > children_;
    };
    

    So hätte man in den meisten Fällen auch keine 2 Speichersprünge von Kind-Element zu Impl, was ich echt gerne vermeiden würde.
    Irgendwie scheint mir die effiziente Trennung der beiden (Im Gegensatz zum einfachen Ableiten "MyNode : public Node") gar nicht so recht einfach, ja schon fast irgendwie unmöglich das irgendwie vergleichbar hinzubekommen...


Log in to reply