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