Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Wrapper: A Bridge for Class Properties

4.64/5 (3 votes)
28 Oct 2019CPOL4 min read 3.7K   56  
Wrapper: A Bridge for Class Properties

Motivation

Not very long ago, I was tasked with interfacing a text-driven rule processor with our product. In order to get it to work, I needed to provide data from our product to the processor. Our product has several classes which contain an obscene amount of data. One class has nearly 250 fields. The rule processor would ask for data that it needs and we would find the class, find the ‘get’ property method on the class and marshall the data over. Of course, the problem is determining which data out of the hundreds of field across several objects and that would require a lookup table, which I was not keen on writing.

So what I wrote was a wrapper class for our data classes (which do a lot more than storing data) to provide a lookup mechanism for the getters and setters of the fields and to provide an array interface on those methods.

This method uses object members and templatizes them so that the methods themselves can be abstract away.

Besides, I was looking for a good example to use templates with class non-static members.

The Fluff

Class Variable is just a generic box object, similar to the VARIANT structure used by Microsoft and COM back in the old days. I just scaled it way down. The reason for it is to have a standard input and output to the Wrapper class. I could have done the code without it but this makes everything easier.

Class PropertyException is just an exception class. Not much to see here.

Struct DeleteMappedObject and PrintObject are just some helpers for cleaning up and printing.

Classes Object1 and Object2 are the target classes that we will use to test out the Wrapper. They each have three properties. They inherit from an interface template class Target<T> which provides a method for transferring the getters and setters to the Wrapper.

The Stuff

setMethod and getMethod are template structures which will hold the class method for setting or getting a value from the object. I have defined the basic method we will need, a union struct because we only need one of them and a variable_type parameter to know what kind of value we expect from the class method. Basically:

C++
template <class T>
struct setMethod
{
       typedef void (T::*setDouble)(double);
       typedef void (T::*setBoolean)(bool);
       typedef void (T::*setInteger)(int);
       typedef void (T::*setCharacter)(char);
       typedef void (T::*setString)(const std::string&);

       union MethodType
       {
              void*                pointer;
              setDouble            sD;
              setBoolean           sB;
              setCharacter         sC;
              setInteger           sI;
              setString            sS;
       } m_type;
       Variable::variable_type m_vt;
…

}

Additionally, there are several setMethod() calls to set the method in m_type and a method called set() which passes the variable to the method on the object:

C++
void set(Variable & _value, T * _ptr)
{
   if (_ptr)
   {
      switch (m_vt)
      {
      case Variable::v_float:
         (_ptr->*(m_type.sD))((double)_value);
         break;
      case Variable::v_boolean:
         (_ptr->*(m_type.sB))((bool)_value);
         break;
      case Variable::v_character:
         (_ptr->*(m_type.sC))((char)_value);
         break;
      case Variable::v_integer:
         (_ptr->*(m_type.sI))((int)_value);
         break;
      case Variable::v_string:
         (_ptr->*(m_type.sS))((const std::string&)_value);
         break;
      }
   }
}

For example:

C++
(_ptr->*(m_type.sD))((double)_value)
  • ptr is the object that we are wrapping.
  • m_type is the union struct for the method types.
  • sD is the setDouble() method type.
  • *(m_type.sD) is the dereferenced method on the object.
  • _value is the value that we are passing into sD as defined by its object.

The template struct getMethod is similar to setMethod.

The template class Target is a simple interface class inherited by the Object class to provide a method which passes the get and set methods of the class to the Wrapper class. There may be a better way of handling this than inheriting a class interface but for this article, it is how I built it to make it simple.

Class Property is an interface class which defines get and set operator on the class.

C++
class Property
{
public:
    Property() {}
    virtual ~Property() {}

    virtual operator Variable() = 0;
    virtual Variable & operator = (Variable &) = 0;
};

Template class TemplateProperty inherits from class Property. It maintains the get and set methods as well as the name used for the get and set methods and it implements the get and set operators from Property interface.

The Wrapper class is where everything is tied together. It maintains a list of names to reference the methods. It maintains a map of Property objects. It has a method for adding the get and set methods to the map of Property objects. It can tell you if a Property object exists for a specific name. It has an array operator to allow you to access the Property objects by name so that you can get or set values for the method in the Property object. It also maintains the object that it wraps.

C++
class Wrapper
{
public:
   typedef std::list<std::string> NameList;
   typedef std::map<std::string, Property*> PropertyMap;
public:

...
   Property& operator[](const std::string& _name)
   {
      Property* p = nullptr;
      if (exists(_name))
         p = map[_name];
      else
         p = map["NULL"];
      dynamic_cast<TemplateProperty<T>*>(p)->setObject(ptr);
      return *p;
   }
private:
   NameList list;
   PropertyMap map;
   T* ptr;
};

The Property is returned but you can access its get and set methods in code:

C++
double vx = (double)V(w2[X]);

I use the macro V(x) to apply a static_cast to property to get the Variable object that maintains.

Finally, there is the WrapperTest where we see how to instantiate and call methods on the Wrapper object. The expected output is in the source code.

I have tested this on CentOS 6 and Windows 10. The CMakeLists.txt is what I have used to build the binaries. I have used this in Linux and it also works with Visual Studio 2019 Community Edition.

History

  • 29th October, 2019: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)