Tag: #pattern

C++

Overload operators wisely – proxy class pattern

One of the key features of C++ is operator overloading. Beginners might find it difficult since the syntax is not intuitive. In this article, I will show you how to omit typical problems in this case.

In my last project, I had to operate on large XML files – read and write some values under specific XPath locations. during development, I have used libxml2 library. It has a really complicated C-based interface, so I had to create a convenience wrapper class.

I used C++ so I decided to use the index operator to fetch values under a specific key. The first interface looked like the following:

class XmlWrapper
{
public :
   //...
   std::string operator[](const std::string &key) const; //getter
   std::string& operator[](const std::string &key); //setter
   //...
};

What is wrong with this approach? At the first sight, it might look ok, but if you start implementation of setter, you will find out, that changing the content of the in-memory libxml2 tree is impossible. The upper approach would be ok if we would change the class attribute of type std::stringdsdf. Otherwise, this approach fails.

What is the possible solution to this approach? I used a proxy class design pattern. Before I will show you the code I will write down, what were the goals of the solution.

  1. Access in-memory XML tree with setter to modify its content
  2. Assign results of getter bracket operator to the std::string variable without intermediate steps.
  3. Like point 2, but the other way assign to setter type std::string.

To implement goal 1, we must have some connection with XML-tree. String class obviously doesn’t. So the returned proxy class has the reference to XmlWrapper holding libxml2 instrumentation. To implement goal 2 we will implement cast operator from proxy class to std::string. To implement goal 3, we will do a similar thing – overload operator = with std::string parameter. The solution looks like this:

class ProxyXml; //forward declaration
class XmlWrapper
{
public :
   //...
   const ProxyXml operator[](const std::string &key) const; //getter
   ProxyXml operator[](const std::string &key); //setter
   //...
};
class ProxyXml
{
   //...
   operator std::string() const;
   ProxyXml& operator=(const std::string &s);
protected :
   XmlWrapper &xml_;
};

If we want to access XML content for read, the code creates a temporary ProxyXml object, and convert it to std::string class. If we want to modify XML content we create ProxyXml and use its XmlWrapper reference inside operator=. Sample usage looks like this:

int main(int argc, const char *argv[])
{
   XmlWrapper xml("path/to/file.xml");
   std::string somevalue = xml["keytosomevalue"]; //getter
   somevalue.append("appendedtext");
   xml["keytosomevalue"] = somevalue; // setter with updated content
   return 0;
}