Skip to content

Instantly share code, notes, and snippets.

@berdon
Last active November 18, 2023 17:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save berdon/98f8c5bc631afa9542b2705617812d4f to your computer and use it in GitHub Desktop.
Save berdon/98f8c5bc631afa9542b2705617812d4f to your computer and use it in GitHub Desktop.
C++20 properties definition file that enables getter/setter style properties.
#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
@rpolitex
Copy link

rpolitex commented Nov 18, 2023

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?

@berdon
Copy link
Author

berdon commented Nov 18, 2023

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.

@rpolitex
Copy link

Thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment