Mittwoch, 2. Juni 2021

[C++] Properties in Classes

 In Java, people write getter and setter for lots of their class attributes. Often enough, that there is a Getter and Setter annotation available, that makes the code much less verbose. (Still the question: why not making it public than...). Pythons properties in that case are much nicer, as you still simply access and use the variable, but it can do some functionality in the background. With the expanse, that you don't directly see, if any, probably heavy, side-effects would be triggered.

As in C++ there is neither, no direct support for either the Java-style-annotation or the Python-style-decorator in the standard or by a small prominent library (at least I didn't hear or see any of it in recent years), I did a little hacky implementation (Compiler Explorer): 

#include <functional>

template<typename T>
struct Property {
    T store;
    std::function<auto (T &) -> T> getter;
    std::function<auto (T &, T) -> void>  setter;
    Property(
        T init,
        std::function<auto (T &) -> T> getter,
        std::function<auto (T &, T) -> void>  setter)
    : store(init)
    , getter(getter)
    , setter(setter)
    {}

    auto operator=(T val) -> void {
        this->setter(store, val);
    }

    operator T() {
        return getter(store);
    }
};

struct Tester{
    Property<int> pie = Property<int>(
            0,
            [](auto & value){ return value; },
            [](auto & value, auto newValue){ value = newValue; });

};


Checking the internet afterwards, I saw other people having similar implementations, with critics warning of performance penalties. And also people not liking properties at all.


There are always people with different tastes. You can't change that or those. But problems with performance can be addressed, I thought. So I adjusted my first implementation to use macros instead (Compiler Explorer): 

#define GETTER(getter) { getter; }
#define SETTER(setter) { setter; }

#define PROPERTY(type, name, getter, setter) \
    struct { \
        private: type _##name; \
        public: operator type() getter; \
        public: auto operator=(type const & value) -> void setter; \
    } name;

struct Tester{
    PROPERTY(int, pie, GETTER( return _pie ), SETTER( _pie = value ));
};


How about combining both? Basically creating a macro, which creates templates for different property constellations (Compiler Explorer). This way, specialized properties can be defined and used in certain places.

#define Getter(getter) { getter; }
#define Setter(setter) { setter; }
#define PropertyTemplate(name, getter, setter)      \
    template<typename T>                            \
    class name {                                    \
    private: T _value;                              \
    public:                                         \
        explicit name(T init) : _value(init) {}     \
        auto operator=(T value) -> void { setter; } \
        operator T() { getter; }                    \
    };


PropertyTemplate(SimpleProperty, Getter(return _value), Setter(_value = value))

struct Tester{
    SimpleProperty<int> pie = SimpleProperty<int>(0);
};


The usage for all those different implementation look the same:

auto main() -> int {
    auto tester = Tester();
    tester.pie = 41;
    auto myInt = static_cast<int>(tester.pie);
    return myInt;
}


It seems, there could be some ways to put it together nicely and add it to the standard. Not using std::function but directly some structs makes the performance penalty go low (at least in this simple example). And using some form of custom "Property"-type (currently for me it is just a struct) directly lets you know, that there might be some side-effects happening instead of thinking of a simple access. (Also, you can simple write custom operator for = and cast operator, which will hide side-effects.) 

Keine Kommentare:

Kommentar veröffentlichen

[Review/Critic] UDock X - 13,3" LapDock

The UDock X - 13.3" LapDock is a combination of touch display, keyboard, touch-pad and battery. It can be used as an extension of vari...