Detecting change to an object in C++

published at 26.11.2021 17:20 by Jens Weller
Save to Instapaper Pocket

This week I wrote down some example code to play a bit around with how one can detect change in an object in C++ during runtime.

A popular idiom in C++ have become public members. It makes a lot of things easier, but also breaks encapsulation. When thinking about this, I remember that in one of my projects I do have setters which not only set one value but two. In my CMS I have a boolean member which gets set to true when a setter is called. In the code base that happens in all most all setters. The default of the value is false. This allows to easily check if a node in a tree has been changed. I plan to do a bit of a rewrite of my CMS in 2022-24, and this is one of the things I'm looking at currently.

For some time I've been thinking if it makes sense to wrap the member variable into a type, that handles certain things, like setting this bool variable. So I wrote a basic implementation, to have something to convey the idea and serve as the base of a debate.

Here it is:

template<class T, class Changed = bool >
class ChangeDetector
{
    T t;
    Changed changed=false;
public:
//constructors left out for clarity
    const T* operator->()const{return &t;}
    const T& get()const{return t;}
    T& make_change(){changed = true; return t;}
    bool hasChanged()const{return changed;}
};

And here would be its usage in a type with the need to detect change:

struct example
{
    ChangeDetector<std::string> name;
    bool hasChanged()const{return name.hasChanged();}
};

If I'd wanted to explore this direction more I'd dedicate a bit of time to make the changed variable either a reference when called by a special constructor or defaulting to a normal member. bool or std::atomic<bool> might be used as this code might be called in multithreaded environments, but likely bool would also be fine.

The above type then will allow you to either query a const reference to read the type or to get non const ref, triggering a write to the changed member. For lazyness I also implemented an op->, as reading should be the norm for most types.

I did post this code on twitter and other social networks, which spawned some interesting discussions and feedback. Its also why I wrote it on Compiler Explorer, I wanted to pick a few brains thoughts on this.

The above code comes close to a property like type. And in a different use case I use boost::signal in my CMS to notify other objects of a changed that happend. Though for this code the use case is more here is the node that represents a file, and all child nodes of this node could have changed. If one has changed, this file should be written to disc. But writing each change on its own to disc isn't the right thing to do, neither would be to write every nth change or in the next time slot.

But having a good library for property like types in C++17 would be awesome. Coincidence that KDAB just released and a user pointed in the twitter thread to this library: KDBindings. Its an interesting candidate for the object to object notifications, currently I use boost::signals2 for this. Though this has some very long linking times, but is thread safe and mature.

But slapping a Property type over every member variable and then implementing the onChanged signal for this type does not solve my problem in a nice way. My example code has one property which I do like very much: its precisely only the functionality I'm after. Also I think its not very pretty, it works, and I don't have to implement dozends of getter/setter pairs. And some functionality could be added to make things simpler. But mostly it shows the current functionality in a simlpe example to make folks understand what this is about.

One drawback as of now I think that this solution can't get rid of the reference to the type or callback to set to true for a change that occurred. So every type in my class gets a little bit bigger, even if its only a byte in this case per member. This could cause some alignment issues. Also the fact that we wrap each member in a class type might not give the greatest response from the optimizer and other code tools.

So one has to wonder if there could be a better way.

Hashing

One idea Peter Sommerlad brought up, if I had thought about using a hash to detect the change. And he is right, thats an interesting idea. After all I'd like to know if an object has changed, and in some passive way when a certain process is triggered scanning for these changes. So being notified of a change is a different use case, which this solution can't cover.

A first sketch of how this could look like:

struct hashexample
{
    const size_t lastmemberhash;
    size_t currentmemberhash;
    size_t hashMembers();
bool hasChanged()const{return lastmemberhash != currentmemberhash;};
//constructors };

The type would initialize lastmemberhash when it is constructed/loaded from disc. When one wants to know if a change has happend, one can recalculate the hash with hashMembers. For the hash it self, std::hash with boost::hash_combine would do the trick.

This not only gets rid of the getters and setters, but also will only calculate this on demand. Though then it will have a certain overhead. This allows public members, and one can still be using a property like type for when its needed.

And last but not least, while I implemented the feature in my CMS, I don't use it. As I never implemented the part of writing only the changes of the model. I've opted to just write all in once. But over time my website has grown and keeps growing, and I do see the benefit of being able to only write the changed things out into a directory. So its a feature worth keeping and implementing now.

 

Join the Meeting C++ patreon community!
This and other posts on Meeting C++ are enabled by my supporters on patreon!