#ifndef VECTOR2_H #define VECTOR2_H #include #include #include #include template struct Property { T _val; // make it an aggregate operator T() const& noexcept { return _val; } operator T() && noexcept(std::is_nothrow_move_constructible_v) { return std::move(_val); } Property& operator=(const T& x) & noexcept(noexcept(_val = x)) { _val = x; update(); return *this; } Property& operator+=(const T& x) & noexcept(noexcept(_val += x)) { _val += x; update(); return *this; } Property& operator-=(const T& x) & noexcept(noexcept(_val -= x)) { _val -= x; update(); return *this; } Property& operator*=(const T& x) & noexcept(noexcept(_val *= x)) { _val *= x; update(); return *this; } Property& operator/=(const T& x) & noexcept(noexcept(_val /= x)) { _val /= x; update(); return *this; } Property& operator++() & noexcept(noexcept(++_val)) { ++_val; update(); return *this; } T operator++(int) & noexcept(noexcept(_val++)) { T copy = _val; ++(*this); return copy; } private: void update() { std::invoke(update_func, get_owner()); } Owning_Object& get_owner() { return *reinterpret_cast(reinterpret_cast(this) - offset); // This doesn't really belong here, but it needs to be in a complete class // context and this is the member function most certain to be instantiated static_assert(sizeof(Property) == sizeof(T)); } }; template class Vector2 { private: void update_polar() { len._val = std::sqrt(x * x + y * y); dir._val = std::atan2(y, x); } void update_cart() { x._val = len * std::cos(dir); y._val = len * std::sin(dir); } public: Property x{}; Property y{}; Property len{}; Property dir{}; }; #if 0 template struct Property_b { operator T() const& noexcept {return _val;} operator T() && noexcept(std::is_nothrow_move_constructible_v) {return std::move(_val);} Property& operator=(const T& x)& noexcept(noexcept(_val = x)) { _val = x; update(); return *this; } Property& operator+=(const T& x)& noexcept(noexcept(_val += x)) { _val += x; update(); return *this; } Property& operator-=(const T& x)& noexcept(noexcept(_val -= x)) { _val -= x; update(); return *this; } Property& operator*=(const T& x)& noexcept(noexcept(_val *= x)) { _val *= x; update(); return *this; } Property& operator/=(const T& x)& noexcept(noexcept(_val /= x)) { _val /= x; update(); return *this; } Property& operator++()& noexcept(noexcept(++_val)) { ++_val; update(); return *this; } T operator++(int)& noexcept(noexcept(_val++)) { T copy = _val; ++(*this); return copy; } T _val; // make it an aggregate private: constexpr void update() { std::apply(update_func, std::tuple{get_owner()}); } constexpr Owning_Object& get_owner() { static_assert(sizeof(Property) == sizeof(T)); return *reinterpret_cast(reinterpret_cast(this)-offset); } }; template class Vector2b { private: void update_polar() { len._val = std::sqrt(x*x + y*y); dir._val = std::atan2(y, x); } void update_cart() { x._val = len * std::cos(dir); y._val = len * std::sin(dir); } public: Property x{}; Property y{}; Property len{}; Property dir{}; }; #endif #endif // VECTOR2_H