Menu

Is it save to copy a TiXmlElement?

2006-01-02
2013-05-20
  • Thomas Trummer

    Thomas Trummer - 2006-01-02

    Hi,

    I want to do the following:

    1) Open XML file
    2) Get all elements I need
    3) Copy them to a list for further use
    4) Close the document

    Now the problem is how to copy the TiXmlElement. Clone isn't really an option since you only get a pointer and not an actual object (which is very error prone). The assignment operator is protected and therefore also for no use.

    Can this be solved?

    Regards
    Thomas

     
    • Ellers

      Ellers - 2006-01-02

      For part (3), do you mean keep a list of TiXmlElement objects like

        std::vector<TiXmlElement*>

      or add them to an existing list, like

        TiXmlElement container;
        container.LinkEndChild(pElement)

      ?

      Either way I'm presuming that you mean you want to keep the TiXmlElement object, not just the text or some value within it.

      You have a few options.
      But please note there is not much here that is TinyXml-specific; its just plain C++ linked-list/ptr manipulation.

      Option 1: if you don't want to keep your origin DOM tree intact, you could unlink the element from the tree, then add it to your new tree. However, having just browsed RemoveChild(), it looks like 'unlink' isn't supported, b/c RemoveChild() will delete the child.

      Option 2: just use Clone(). OK it risks being a bit heavy on the memory but in general use (of mine anyway) this has never been a problem.

      Your code will look vaguely like:

      void findElementsINeed(TiXmlNode* pNode, TiXmlElement* pContainer)
      {
        // *psuedocode*
        if (isWhatIWant(pNode)) {
          pContainer->LinkEndChild(pNode->Clone());
        }
        // and traverse all children now.
        for each pChild of pNode {
          findElementsINeed(pChild, pContainer);
        }
      }

      Note that the ref version of LinkEndChild() does the clone for you, so you prefer not to see a clone (it'll see be done, just hidden) then you can do:

        if (isWhatIWant(pNode)) {
          pContainer->LinkEndChild(*pNode);
        }

      (I haven't tested that but based on the code it should work)

      I don't follow what you mean about Clone() not being an option. All of a TinyXml node tree is made up of "actual objects" being pointed to by pointers. Can you clarify what you mean?

       
    • Thomas Trummer

      Thomas Trummer - 2006-01-05

      I'm trying to save the TiXmlElement's in an STL container (as an member of another class). These elements have to stay valid after the original XML file is closed. Can this be achieved by using Clone() (the docs are a bit unclear about this)? The problem with Clone() is you only get a pointer, so you have to wrap it in a smart pointer if you want to store it in an STL container (at least if you have to delete the pointer yourself).

       
      • Ellers

        Ellers - 2006-01-05

        Hmmm - No.

        Clone() clones ;)
        The source shows its a deep copy.
        The object is, AFAIK, owned by the caller, not the existing document tree.

        STL holds ptrs just fine. In fact, some people say its *smart ptrs* that STL has problems with.

        typedef std::vector<TiXmlElement*> TiXmlNodeVec;

        TiXmlNodeVec vec;
        vec.push_back(pNode->Clone());

        or did you mean something else?

        Right here I started writing about how you could store a "real object" (whatever you mean by that) by dereferencing the ptr like this:

        TiXmlNode* pCopy=pNode->Clone();
        vec.push_back(*pCopy) // BAD

        However, for this to work firstly you'd need to implement the copy constructor and = operator for all classes, and secondly and much more significantly, you can't contain objects like that in this kind of list, or any kind that I know of.

        For example, if you do:

        std::vector<TiXmlNode>

        then it can't contain TiXmlElement objects. Kinda obvious really. But the point is that a list of TiXmlNode ptrs is exactly what you need.

        PS
        Remember to delete all ptrs in the list before the list is destroyed. Traverse the list and delete what each item points to:

        Here is the templated routine that I use that will work with a STL vector (and could work with other containers with minor changes):

        template <class T>
        void vector_delete_ptrs( vector<T> & vec )
        {
            vector<T>::iterator iter;
            for ( iter = vec.begin(); iter != vec.end(); iter++ )
            {
                T item = *iter;
                delete item;
                *iter = 0;
            }
        }

        example:

        {
        TiXmlNodeList vec;
        vec.push_back(pNode->Clone());

        // stuff...

        // before destructor
        vector_delete_ptrs<TiXmlNode*>(vec);
        }

        HTH

         
    • Lee Thomason

      Lee Thomason - 2006-01-06

      Also, the *Element* operator is public, put the *Node* is not. Your should be able to assign

      myElementArray[0] = *elementPointer;

      just fine. Obviously for assignment (a copy) both types have to be the same, so you have to watch your types.

      lee

       

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.