Search code examples
c++tinyxml

Getter and setter wrappers for TiXmlElement*'s


I am rewriting a project so that it uses getters and setters to reference the TiXmlElement *'s However, I quickly run into problems that seem to be related to debug mode:

Ecxerpt from my class's header:

TiXmlElement *_rootElement;
TiXmlElement *_dialogsElement;
TiXmlElement *_dialogElement;
TiXmlDocument _document;
void setDocument (TiXmlDocument doc) { this->_document = doc; }
void setRootElement (TiXmlElement * element) { this->_rootElement = element; }
void setDialogsElement (TiXmlElement * element) { this->_dialogsElement = element; }

TiXmlDocument getDocument () { return this->_document; }
TiXmlElement* getRootElement () { return this->_rootElement; }
TiXmlElement* getDialogsElement () { return this->_dialogsElement; }

Excerpt from class constructor:

DCXML::DCXML(const char *dialogMark,const char *dialogName,TiXmlDocument doc) {
...
this->setDocument(doc);
this->setRootElement(this->getDocument().FirstChildElement("dcxml"));
this->setDialogsElement(this->getRootElement()->FirstChildElement("dialogs"));

Excerpt from instantiating the class:

TiXmlDocument doc(input.gettok(2,"\"").to_chr());
bool dcxmlFile = doc.LoadFile();
...
DCXML *dcxml = new DCXML(input.gettok(2).to_chr(),input.gettok(3).to_chr(),doc);

Now for the weird part. This works up until

this->setDialogsElement(this->getRootElement()->FirstChildElement("dialogs"));

in the constructor.

->FirstChildElement("dialogs") throws an "CXX0039: Error: symbol is ambiguous" error in VS2008 when in debug mode.

The weird part is IntelliSense picks up on the FirstChildElement method and the compiler doesn't throw any errors.

What's even weirder is that when in release mode it just fails silently to get the dialogs element.

What I am doing wrong ? Or if you have succesfully implemented getter setter wrappers for TiXmlElement*'s let me know how I can as well!.

For complete reference here's an excerpt from the XML file:

<?xml version="1.0" encoding="utf-8"?>
<dcxml>
    <dialogs>
        <dialog 
            name="mediaplayer" 
            center="" 
            w="300" 
            h="400" 
            caption="Mamp 4.0 BETA" 
            border="btmnzy">
        </dialog>
    </dialogs>
</dcxml>

Feedback would be much appreciated as I am at a dead end :)


Solution

  • Make sure that

    TiXmlDocument getDocument () { return this->_document; } 
    

    Will not deep copy its contained TiXmlElement's. Otherwise you return a temporary, use that in the constructor to set the root node, which then will be destructed already. I haven't looked in its API, but just be aware of such pitfalls.

    The reason for the ambiguous call is because:

    There are three overloads of FirstChildElement taking one argument:

    const TiXmlElement *    FirstChildElement (const char *value) const // :1
    const TiXmlElement *    FirstChildElement (const std::string &_value) const // :2
    TiXmlElement       *    FirstChildElement (const std::string &_value) // :3
    

    You access the TiXmlElement through TiXmlElement& (using a TiXmlElement* pointer). But the version taking a const char* has an implicit object parameter of TiXmlElement const&. That is, a qualification conversion is required to make the call work. For the other versions taking a std::string const&, there are conversions required too:

    <implied obj param> <implicit obj param>    <arg1>         <param1>
    TiXmlElement&        TiXmlElement const&    char const*    char const*         // :1
    TiXmlElement&        TiXmlElement const&    char const*    std::string const&  // :2
    TiXmlElement&        TiXmlElement&          char const*    std::string const&  // :3
    

    There is an ambiguity between the first and the third overload. An easy fix is to do

    this->setDialogsElement(
        this->getRootElement()->FirstChildElement(std::string("dialogs")));
    

    Instead, which will call the last version. Another fix is to const_cast:

    this->setDialogsElement(
        const_cast<TiXmlElement const*>(this->getRootElement())->
            FirstChildElement("dialogs"));
    

    Which will call the first version. As for why it only happens in DEBUG... I remember that TiXML has an option to disable the use of the STL. Maybe in release mode you disabled it (and thus the overloads taking std::string), but in debug mode you forgot?