Opened 9 years ago

Closed 6 years ago

Last modified 6 years ago

#6143 closed enhancement (fixed)

Propose CPLXMLTreeCloser

Reported by: Kurt Schwehr Owned by: warmerdam
Priority: normal Milestone:
Component: default Version: svn-trunk
Severity: normal Keywords:
Cc:

Description (last modified by Kurt Schwehr)

I propose this as an initial start at making cleanup simpler. This mimics the style of std::unique_ptr.

// Manage a tree of XML nodes so that all nodes are freed when the instance goes
// out of scope.  Only the top level node should be in a CPLXMLTreeCloser.
class CPLXMLTreeCloser {
   public:
    explicit CPLXMLTreeCloser(CPLXMLNode* data) { the_data_ = data; }

    ~CPLXMLTreeCloser() {
        if (the_data_) CPLDestroyXMLNode(the_data_);
    }

    // Modifying the contents pointed to by the return is allowed.
    CPLXMLNode* get() const { return the_data_; }

    CPLXMLNode* operator->() const { return get(); }

   private:
    CPLXMLNode* the_data_;
};

// Example of use in a test gunit test case.
TEST(CplMiniXmlTest, ParseOne) {
    CPLXMLTreeCloser root(CPLParseXMLString("<a/>"));
    ASSERT_NE(nullptr, root.get());
    EXPECT_EQ(CXT_Element, root->eType);
    EXPECT_STREQ("a", root->pszValue);
    EXPECT_EQ(nullptr, root->psNext);
    EXPECT_EQ(nullptr, root->psChild);
}

Note: The above formatted with:

Error: Failed to load processor bash
No macro or processor named 'bash' found

Would be nice to make a clang-format style definition that matches the default GDAL style. http://clangformat.com/

An example of how this can be used to simplify cleanup in functions/methods with multiple return paths needing cleanup. (picking ParseKMLGeometry from ogrgftlayer.cpp at random)

static OGRGeometry* ParseKMLGeometry(const char* pszKML)
{
    CPLXMLNode* psXML = CPLParseXMLString(pszKML);
    if (psXML == NULL)  // Add .get() to fetch the pointer.
        return NULL;

    if (psXML->eType != CXT_Element)  // Member access works the same.
    {
        CPLDestroyXMLNode(psXML);  // This will go away.
        return NULL;
    }

    OGRGeometry* poGeom = ParseKMLGeometry(psXML);  // Add .get() to fetch pointer.

    CPLDestroyXMLNode(psXML);  // This will go away.
    return poGeom;
}

becomes (is the o prefix correct?)

static OGRGeometry* ParseKMLGeometry(const char* pszKML)
{
    CPLXMLTreeCloser oXML = CPLParseXMLString(pszKML);
    if (oXML.get() == NULL)
        return NULL;

    if (oXML->eType != CXT_Element)
    {
        return NULL;
    }

    OGRGeometry* poGeom = ParseKMLGeometry(oXML.get());

    return poGeom;
}

Or even simpler:

static OGRGeometry* ParseKMLGeometry(const char* pszKML)
{
    CPLXMLTreeCloser psXML = CPLParseXMLString(pszKML);
    if (psXML.get() == NULL || psXML->eType != CXT_Element)
    {
        return NULL;
    }

    return ParseKMLGeometry(psXML.get());  // ParseKMLGeometry(CPLXMLNode* psXML).
}

Change History (10)

comment:1 by Kurt Schwehr, 9 years ago

Description: modified (diff)

comment:2 by Even Rouault, 9 years ago

Perhaps CPLXMLTreeHolder instead of CPLXMLTreeCloser ? (to be consistent with CPLMutexHolder)

(Not sure about how the mention of the clang-format relates to this however)

comment:3 by Kurt Schwehr, 9 years ago

clang-format was just an aside.

The pattern that I've usually seen is "closer" for things that dispose of a thing as a part of the destructor. e.g. a file closer. Alternatively, this could be CPLXMLTreeDestroyer.

I'd lean towards reserving "holder" for stuff that doesn't destroy/free the thing that it is wrapping.

comment:4 by Even Rouault, 9 years ago

OK as you see fit

comment:5 by Kurt Schwehr, 9 years ago

Initial commit with one use in trunk: r30973

comment:6 by Even Rouault, 8 years ago

Resolution: fixed
Status: newclosed

comment:7 by Kurt Schwehr, 8 years ago

The class needs a release() method for return conditions where the tree itself is returned in some cases.

comment:8 by Even Rouault, 8 years ago

Resolution: fixed
Status: closedreopened

comment:9 by Even Rouault, 6 years ago

Resolution: fixed
Status: reopenedclosed

In 41735:

Make CPLXMLTreeCloser derive from std::unique_ptr<CPLXMLNode> and use it in gdaljp2metadata.cpp (fixes #6143)

comment:10 by Even Rouault, 6 years ago

In 41739:

CPLXMLTreeCloser: fix memory leak of r41735 (refs #6143). Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=6852. Credit to OSS Fuzz

Note: See TracTickets for help on using tickets.