-
-
Save berdon/98f8c5bc631afa9542b2705617812d4f to your computer and use it in GitHub Desktop.
C++20 properties definition file that enables getter/setter style properties.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#pragma GCC diagnostic push | |
#pragma GCC diagnostic ignored "-Wreturn-stack-address" | |
template <typename T> | |
class Property | |
{ | |
T _value; | |
bool _isAuto = true; | |
/** | |
* Used for auto-property behavior. | |
*/ | |
std::function<const T&(const T&)> _autoGetter = [](const T& value) -> const T& { return value; }; | |
std::function<const T&(T&, const T&)> _autoSetter = [](T& value, const T& newValue) -> const T& { return value = newValue; }; | |
/** | |
* Used for non-auto property behavior. | |
*/ | |
std::function<const T&()> _getter; | |
std::function<const T&(const T&)> _setter; | |
/** | |
* Convenience methods for calling the actual setter/getters regardless of auto-property | |
* or not. | |
*/ | |
const T& set(const T& value) { return _isAuto ? _autoSetter(_value, value) : _setter(value); } | |
const T& get() { return _isAuto ? _autoGetter(_value) : _getter(); } | |
public: | |
struct AutoParams { | |
std::function<const T&(const T&)> get = [](const T& value) { return value; }; | |
std::function<const T&(T&, const T&)> set = [](T& value, const T& newValue) { return value = newValue; }; | |
}; | |
struct Params { | |
std::function<const T&()> get = []() -> const T& { throw new std::exception(); }; | |
std::function<const T&(const T&)> set = [](const T& value) -> const T& { throw new std::exception(); }; | |
}; | |
struct WrappedGetParams { | |
T& get; | |
}; | |
struct WrappedSetParams { | |
T& set; | |
}; | |
// Implicit conversion back to T. | |
operator const T& () { return get(); } | |
const T operator=(T other) { return set(other); } | |
const T operator=(Property<T> other) { return set(other.get()); } | |
Property<T>& operator++() { return set(get()++); } | |
T operator++(int n) { | |
return set(get() + (n != 0 ? n : 1)); | |
} | |
Property<T>& operator--() { return set(get()--); } | |
T operator--(int n) { | |
return set(get() - (n != 0 ? n : 1)); | |
} | |
const T& operator+=(const T& other) { return set(get() + other); } | |
const T& operator-=(const T& other) { return set(get() - other); } | |
const T& operator+(const T& other) { return get() + other; } | |
friend const T& operator+(const T& first, Property<T>& other) { return first + other.get(); } | |
const T& operator-(const T& other) { return get() - other; } | |
friend const T& operator-(const T& first, Property<T>& other) { return first - other.get(); } | |
const T& operator*(const T& other) { return get() * other; } | |
friend const T& operator*(const T& first, Property<T>& other) { return first * other.get(); } | |
const T& operator/(const T& other) { return get() / other; } | |
friend const T& operator/(const T& first, Property<T>& other) { return first / other.get(); } | |
friend std::ostream& operator<<(std::ostream& os, Property<T>& other) { return os << other.get(); } | |
friend std::istream& operator>>(std::istream& os, Property<T>& other) { | |
if (other._isAuto) { | |
return os >> other._value; | |
} | |
else { | |
T ref; | |
os >> ref; | |
other.set(ref); | |
return os; | |
} | |
} | |
// This template class member function template serves the purpose to make | |
// typing more strict. Assignment to this is only possible with exact identical types. | |
// The reason why it will cause an error is temporary variable created while implicit type conversion in reference initialization. | |
template <typename T2> T2& operator=(const T2& other) | |
{ | |
T2& guard = _value; | |
throw guard; // Never reached. | |
} | |
Property() {} | |
Property(T& value) | |
{ | |
_isAuto = false; | |
_getter = [&]() -> const T& { return value; }; | |
_setter = [&](const T& newValue) -> const T& { return value = newValue; }; | |
} | |
Property(AutoParams params) | |
{ | |
_isAuto = true; | |
_autoGetter = params.get; | |
_autoSetter = params.set; | |
} | |
Property(Params params) | |
{ | |
_isAuto = false; | |
_getter = params.get; | |
_setter = params.set; | |
} | |
Property(WrappedGetParams params) | |
{ | |
_isAuto = false; | |
auto get = params.get; | |
_getter = [get]() { return get; }; | |
_setter = [](const T& newValue) -> const T& { throw new std::exception(); }; | |
} | |
Property(WrappedSetParams params) | |
{ | |
_isAuto = false; | |
T& set = params.set; | |
_getter = []() -> const T& { throw new std::exception(); }; | |
_setter = [&set](const T& newValue) { return set = newValue; }; | |
} | |
}; | |
#pragma GCC diagnostic pop |
Hi, @berdon
Nice idea for Properties!
Do you have any License restrictions for other developers to use this code as is or make derivatives from it?
Hi, @berdon Nice idea for Properties!
Do you have any License restrictions for other developers to use this code as is or make derivatives from it?
See https://austinhanson.com/properties-in-c-plus-plus-20/ for the few places it was derived from.
I attest no license for it. Use at will.
Thanks a lot!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I’ll have to see what I can wrangle up from a couple years ago. I ended up playing around with this after working on some arduino stuff which is about all I can remember from the toolchain I was using. I was maybe using clang :/.