1

I have created this interface as part of my c++ learning path. I was especially inspired by this post.

The idea is to use the concrete implementations to perform some actions before and after any change of the wrapped variable.

For instance an implementation may notify an observer when the underlying variable changes, another one could be used as a mock for unit testing and so on.

#pragma once

#define CREATE_MEMORY_CELL_IMPLEMENTATION(NAME)                             \
    template<class T>                                                       \
    class NAME : public MemorySystem::IMemoryCell<T>                        \
    {                                                                       \
    public:                                                                 \
        using MemorySystem::IMemoryCell<T>::operator=;                      \
        using MemorySystem::IMemoryCell<T>::value;                          \
        NAME(T v) : IMemoryCell<T>(v) {}                                    \
    protected:                                                              \
        void beforeSetting() override;                                      \
        void afterSetting()  override;                                      \
    };                                                                      


namespace MemorySystem {

    template<class T>
    class IMemoryCell {
    public:
        IMemoryCell(T v): value(v) {}
        operator T() const { return value; }

        // modifiers
        IMemoryCell& operator=(T v)         { setValue(v); return *this;          }
        IMemoryCell& operator+=(T v)        { setValue(value + v); return *this;  }
        IMemoryCell& operator-=(T v)        { setValue(value - v); return *this;  }
        IMemoryCell& operator*=(T v)        { setValue(value * v); return *this;  }
        IMemoryCell& operator/=(T v)        { setValue(value / v); return *this;  }
        IMemoryCell& operator%=(T v)        { setValue(value % v); return *this;  }
        IMemoryCell& operator&=(T v)        { setValue(value & v); return *this;  }
        IMemoryCell& operator|=(T v)        { setValue(value | v); return *this;  }
        IMemoryCell& operator^=(T v)        { setValue(value ^ v); return *this;  }
        IMemoryCell& operator<<=(T v)       { setValue(value << v); return *this; }
        IMemoryCell& operator>>=(T v)       { setValue(value >> v); return *this; }

        // increment & decrement
        IMemoryCell& operator++()           { setValue(value + 1); return *this; }                  // prefix increment
        IMemoryCell& operator--()           { setValue(value - 1); return *this; }                  // prefix decrement
        T operator++(int)                   { T old = value; setValue(value + 1); return old; }     // postfix increment 
        T operator--(int)                   { T old = value; setValue(value - 1); return old; }     // postfix decrement            


    protected:
        T value;
        virtual void beforeSetting() = 0;
        virtual void afterSetting()  = 0;

    private:
        void setValue(const T& val) {
            beforeSetting();
            value = val;
            afterSetting();
        };
    };
}

Here an example of its usage:

#include "memory.h"

CREATE_MEMORY_CELL_IMPLEMENTATION(testMemory)

template <class T> void testMemory<T>::beforeSetting() {
    std::cout << "Old value: " << value << std::endl;
};

template <class T> void testMemory<T>::afterSetting() {
    std::cout << "New value: " << value << std::endl;
};

What I don't like is that the derived class must explicitly declare the use of the base class assignment operator and "value"member. As you can see my first idea was to use a macro to create a skeleton for the implementation.

This approach has two major drawbacks:

  1. It limits the possibilities in terms of concrete implementation class. For instance a new member cannot be defined inside the skeleton.
  2. The definition of the overridden methods is cumbersome.

What do you think?

An alternative could be to define it as a concrete class and to pass the pre-processing and post-processing functions as pointers to the constructor. Do you think that it can be a better approach?

Thanks!

EmaPrn
  • 21
  • 2

0 Answers0