Ein swap Problem wegen Konstanten


  • Administrator

    Hallo zusammen,

    Zuerst den Aufbau, ich hoffe ich konnte ihn genug kürzen:

    class BasicNode
    {
      Element* const m_parent;
      Document* const m_document;
    
      // Werden beide bei der Erstellung des Objektes gesetzt,
      // werden aber nicht kopiert oder geswapt.
    };
    
    // Ein paar Nodes:
    class Element : public BasicNode
    { }
    
    class Comment : public BasicNode
    { }
    
    // usw.
    
    // Ein etwas spezieller Wrapper:
    class NodeWrapper
    {
    public:
      typedef boost::variant<Comment*, Element*, ...> Node;
    
    private:
      Node m_node;
    
    public:
      // Konstruktoren und Methoden ...
      // Swap wäre möglich, aber leicht unsinnig.
    };
    
    // Zur eigentlichen Problemstelle
    class NodeContainer
    {
    private:
      typedef std::vector<NodeWrapper> Container;
    
    public:
      typedef Container::const_iterator ConstIterator;
      // ...
    
    private:
      Element* m_parentForNewNodes;
      Document* m_documentForNewNodes;
    
      Container m_container;
    
      // ...
    public:
      template<typename NodeT>
      NodeT& push_back(NodeT const& node)
      {
        NodeT* newNode = new NodeT(node, m_parentForNewNodes, m_documentForNewNodes);
        // Die letzten beiden Parameter gehen dann weiter zur BasicNode.
    
        m_container.push_back(NodeWrapper(newNode));
    
        return *newNode;
      }
    
      // ...
    
      void swap(NodeContainer& nodeContainer)
      {
        // WIE? Siehe dazu Text unten, falls das Problem noch nicht klar ist.
      }
    };
    

    Das Problem bei diesem swap ist ja, dass ich nicht einfach den Vektor swappen kann. Das würde zwar am schnellsten gehen, aber die darin gespeicherten Zeiger, weisen dann auf Nodes mit den falschen Angaben zu m_parent und m_document in BasicNode.
    Ich muss also diese konstanten Zeiger verändern. Gut, dann mache ich sie halb nicht mehr konstant, das ist das kleinste Problem. Das grössere ist, dass ich eine öffentliche Methode anbieten müsste, um diese Zeiger zu verändern.
    Dies würde aber bedeuten, dass man von ausserhalb der Bibliothek diese Werte verändern kann, was aber nur dazu führt, dass Fehler erzeugt werden. Die Werte dürfen nicht von ausserhalb geändert werden!
    Was kommt also als nächstes in Frage? Friend, also dass BasicNode sich als friend von NodeContainer deklariert. So könnte ich eine private Methode in BasicNode anbieten, welche NodeContainer bedienen kann. Aber so richtig schön, finde ich diese Lösung nicht. Ich habe noch nie friend verwendet, bisher war es nie nötig, es gab immer einen anderen Weg. Aber hier sehe ich einfach keinen ...

    Aber vielleicht könnt ihr mir weiterhelfen ... 🙂

    Grüssli



  • Wieso willst Du bei einem swap von NodeContainer die parents aller Nodes änderen? Und woher willst Du den neuen parent bekommen? Von der Bennung her würde man meinen, das parent ebenfalls einen Pointer auf seine Kinder hält, das müsste dann irgenwie auch geändert werden.

    Oder geht es Dir nur darum, dass m_parentForNewNodes in NodeContainer mit m_parents der enthaltenen Nodes übereinstimmt?
    Wenn swap für Dich das gleiche bedeutet wie für mich, muss Du natürlich nicht nur den m_container austauschen sonderen auch m_parentForNewNodes und dann besteht Dein Problem überhaupt nicht. Das gleiche gilt für m_documentForNewNodes.

    Trotzdem sind die Zeiger natürlich irgenwie redundant. Evtl. kannst Du das gesamte Design ja nochmal überdenken.

    DJohn



  • @Dravere: ja, klar macht man das über friend. Also zumindest ich würde es so machen. Ist auch nix unsauberes daran. Gut, du koppelst 2 Klassen dadurch sehr eng aneinander, aber immer noch besser als irgendwas public/protected zu machen, oder accessor/mutator Funktionen zu haben die man eigentlich garnicht braucht bzw. garnicht haben sollte.

    Ich habe mir jetzt allerdings NICHT überlegt ob es auch einen einfacheren, besseren Weg gibt.

    Was mir viel schwindliger als die ganze friend Geschichte vorkommt ist der NodeWrapper mit dem boost::variant. Wieso verwendest du nicht gleich einen BasicNode* und castest dann entsprechend?


  • Administrator

    DJohn@work schrieb:

    Oder geht es Dir nur darum, dass m_parentForNewNodes in NodeContainer mit m_parents der enthaltenen Nodes übereinstimmt?

    Genau darum geht es.

    DJohn@work schrieb:

    Wenn swap für Dich das gleiche bedeutet wie für mich, muss Du natürlich nicht nur den m_container austauschen sonderen auch m_parentForNewNodes und dann besteht Dein Problem überhaupt nicht. Das gleiche gilt für m_documentForNewNodes.

    Nein, eben nicht. Grundsätzlich kannst du m_parentForNewNodes und m_documentForNewNodes auch als Konstanten anschauen. Diese werden sich nicht verändern, da das eigentliche Objekt immer noch im gleichen Baum ist, nur dessen Inhalt wird getauscht.

    Deshalb muss ich beim Swap auch den Inhalt korrigieren, damit dieser in den neuen Baum oder die neue Stelle passt.

    @hustbaer,
    Naja, aktuelle habe ich es über friend gelöst. Grundsätzlich funktioniert es sehr gut so, nur fühl ich mich irgendwie Unwohl bei der Benutzung ... vielleicht einfach ungewohnt 🙂

    Zum NodeWrapper:
    Eigentlich soll er nur ein paar Dinge absichern, damit nicht gleich mit dem Boost.Variant Objekt rumgespielt werden kann. Aber du fragst ja hauptsächlich wegen des Boost.Variants.

    Die Sache ist die, ich brauche jeweils die entsprechenden Nodes. Grundsätzlich brauche ich BasicNode so gut wie nie. Die Nodes müssen aber auch in eine korrekte Reihenfolge gebracht werden. NodeWrapper ist also eine Art von Position in einer Liste.
    Zudem kann ich komplett auf dynamic_cast verzichten. Ich habe mir entsprechende Acceptors gebaut, welche einem erlauben, über nur eine Art von Nodes (oder mehreren) zu iterieren.
    Als Beispiel mal einen NodeTypeAcceptor:

    //////////////////////////////////////////////////////////////////////////
    // NodeTypeAcceptor : Header file
    
    #ifndef _DSIXML_H_NODE_TYPE_ACCEPTOR_GUARD__
    #define _DSIXML_H_NODE_TYPE_ACCEPTOR_GUARD__
    
    #include "NodeWrapper.hpp"
    
    namespace dsi { namespace xml {
    
    	//////////////////////////////////////////////////////////////////////////
    	// NodeTypeAcceptor
    
    	template<typename ValidNodeT>
    	class NodeTypeAcceptor
    	{
    		// Typedefs //
    	public:
    		typedef ValidNodeT ValidNode;
    
    		// Inner classes //
    	private:
    		class ValidateNode
    		{
    			// Typedefs //
    		public:
    			typedef void result_type;
    
    			// Attributes //
    		private:
    			NodeTypeAcceptor<ValidNode>* m_acceptor;
    
    			// Constructors & Destructor //
    		public:
    			ValidateNode(NodeTypeAcceptor<ValidNode>* acceptor);
    			~ValidateNode();
    
    			// Operators //
    		public:
    			void operator ()(ValidNode* validNode) const;
    
    			template<typename InvalidNodeT>
    			void operator ()(InvalidNodeT*) const;
    		};
    
    		// Attributes //
    	private:
    		ValidNode* m_validNode;
    
    		// Constructors & Destructor //
    	public:
    		NodeTypeAcceptor();
    		~NodeTypeAcceptor();
    
    		// Operators //
    	public:
    		bool operator ()(NodeWrapper const& node);
    
    		// Methods //
    	public:
    		bool check(NodeWrapper const& node);
    		ValidNode* get_node() const;
    	};
    
    } } // dsi::xml
    
    //////////////////////////////////////////////////////////////////////////
    // Template implementations
    
    namespace dsi { namespace xml {
    
    	//////////////////////////////////////////////////////////////////////////
    	// NodeTypeAcceptor::ValidateNode
    
    	/************************************************************************/
    	/* Constructors & Destructor                                            */
    	/************************************************************************/
    
    	template<typename ValidNodeT>
    	NodeTypeAcceptor<ValidNodeT>::ValidateNode::ValidateNode(NodeTypeAcceptor<ValidNode>* acceptor)
    		: m_acceptor(acceptor)
    	{
    	}
    
    	/************************************************************************/
    
    	template<typename ValidNodeT>
    	NodeTypeAcceptor<ValidNodeT>::ValidateNode::~ValidateNode()
    	{
    	}
    
    	/************************************************************************/
    	/* Operators                                                            */
    	/************************************************************************/
    
    	template<typename ValidNodeT>
    	void NodeTypeAcceptor<ValidNodeT>::ValidateNode::operator ()(ValidNode* validNode) const
    	{
    		m_acceptor->m_validNode = validNode;
    	}
    
    	/************************************************************************/
    
    	template<typename ValidNodeT>
    	template<typename InvalidNodeT>
    	void NodeTypeAcceptor<ValidNodeT>::ValidateNode::operator ()(InvalidNodeT*) const
    	{
    		m_acceptor->m_validNode = nullptr;
    	}
    
    	//////////////////////////////////////////////////////////////////////////
    	// NodeTypeAcceptor
    
    	/************************************************************************/
    	/* Constructors & Destructor                                            */
    	/************************************************************************/
    
    	template<typename ValidNodeT>
    	NodeTypeAcceptor<ValidNodeT>::NodeTypeAcceptor()
    		: m_validNode(nullptr)
    	{
    	}
    
    	/************************************************************************/
    
    	template<typename ValidNodeT>
    	NodeTypeAcceptor<ValidNodeT>::~NodeTypeAcceptor()
    	{
    	}
    
    	/************************************************************************/
    	/* Operators                                                            */
    	/************************************************************************/
    
    	template<typename ValidNodeT>
    	bool NodeTypeAcceptor<ValidNodeT>::operator ()(NodeWrapper const& node)
    	{
    		return check(node);
    	}
    
    	/************************************************************************/
    	/* Methods                                                              */
    	/************************************************************************/
    
    	template<typename ValidNodeT>
    	bool NodeTypeAcceptor<ValidNodeT>::check(NodeWrapper const& node)
    	{
    		ValidateNode validater(this);
    		node.apply_visitor(validater);
    
    		return m_validNode != nullptr;
    	}
    
    	/************************************************************************/
    
    	template<typename ValidNodeT>
    	typename NodeTypeAcceptor<ValidNodeT>::ValidNode* NodeTypeAcceptor<ValidNodeT>::get_node() const
    	{
    		return m_validNode;
    	}
    
    } } // dsi::xml
    
    #endif // _DSIXML_H_NODE_TYPE_ACCEPTOR_GUARD__
    

    Dieser kann man dann mit einem RangeIterator kombinieren. Dadurch kann man solche Dinge erstellen:

    template<typename NodeT, typename IterT>
    	util::RangeIterator<IterT, NodeTypeAcceptor<NodeT> > create_node_type_iterator(IterT first, IterT last)
    	{
    		return util::RangeIterator<IterT, NodeTypeAcceptor<NodeT> >(first, last);
    	}
    

    RangeIterator aktzeptiert dann nur solche Nodes, welche von NodeTypeAcceptor als korrekt angegeben wurden. Die anderen werden übersprungen.
    Dieses überspringen geht sehr rasch, dank den Templates. Es wird nie ein dynamic_cast oder etwas ähnliches benötigt. Ich muss dank den Templates nie irgendetwas casten. Absolute Typsicherheit ... geradezu fanatisch 😃

    Ich weiss jetzt nicht, ob es mit diesem Code bereits klar wird. Womöglich nicht ...
    Vielleicht müsste ich irgendwann mal die ganze XML Bibliothek zeigen, damit man die Verwendung kennen lernt. Allerdings will ich zuvor noch ein paar Dinge erledigen ... ein paar 🙂

    Grüssli


Anmelden zum Antworten