Created
April 9, 2020 19:33
-
-
Save strega-nil/062915215ac629c214cd6b2163c0e3fd to your computer and use it in GitHub Desktop.
This file contains hidden or 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
// To the extent possible under law, Nicole Mazzuca has waived all copyright and related or neighboring rights to properties.cxx. | |
#include <utility> | |
#include <stddef.h> | |
// MUST be marked with [[no_unique_address]] | |
template <class Get> | |
struct Property { | |
using containing_type = typename Get::containing_type; | |
template <class T> | |
decltype(auto) operator=(T&& v) const& { | |
return Get{}(containing_class()) = std::forward<T>(v); | |
} | |
template <class T> | |
decltype(auto) operator=(T&& v) const&& { | |
return Get{}(std::move(*this).containing_class()) = std::forward<T>(v); | |
} | |
template <class T> | |
decltype(auto) operator=(T&& v) & { | |
return Get{}(containing_class()) = std::forward<T>(v); | |
} | |
template <class T> | |
decltype(auto) operator=(T&& v) && { | |
return Get{}(std::move(*this).containing_class()) = std::forward<T>(v); | |
} | |
operator decltype(auto)() const& { | |
return Get{}(containing_class()); | |
} | |
operator decltype(auto)() const&& { | |
return Get{}(std::move(*this).containing_class()); | |
} | |
operator decltype(auto)() & { | |
return Get{}(containing_class()); | |
} | |
operator decltype(auto)() && { | |
return Get{}(std::move(*this).containing_class()); | |
} | |
Property() = default; | |
Property(Property const&) = delete; | |
Property(Property&&) = delete; | |
// these can only be self-assign | |
Property& operator=(Property const& p) = default; | |
Property& operator=(Property&& p) = default; | |
~Property() = default; | |
private: | |
static containing_type* containing_class_internal(Property const* p) { | |
// const_cast is okay because it's only ever called by the four functions proceeding | |
// which are const-safe. We const_cast to remove extra code | |
auto byte_ptr = static_cast<void*>(const_cast<Property*>(p)); | |
return static_cast<containing_type*>(byte_ptr); | |
} | |
containing_type const& containing_class() const& { | |
return *containing_class_internal(this); | |
} | |
containing_type const&& containing_class() const&& { | |
return std::move(*containing_class_internal(this)); | |
} | |
containing_type& containing_class() & { | |
return *containing_class_internal(this); | |
} | |
containing_type&& containing_class() && { | |
return std::move(*containing_class_internal(this)); | |
} | |
}; | |
#define CHECK_PROPERTY_OFFSET(CLS, NAME) \ | |
static_assert(offsetof(CLS, NAME) == 0) | |
template <class Cls, class Ty, size_t size, Ty (Cls::* Ptr)[size], size_t Index> | |
struct ArrayMemberGetter { | |
using containing_type = Cls; | |
static_assert(Index < size); | |
Ty const& operator()(Cls const& cls) const { | |
return (cls.*Ptr)[Index]; | |
} | |
Ty const&& operator()(Cls const&& cls) const { | |
return std::move((cls.*Ptr)[Index]); | |
} | |
Ty& operator()(Cls& cls) const { | |
return (cls.*Ptr)[Index]; | |
} | |
Ty&& operator()(Cls&& cls) const { | |
return std::move((cls.*Ptr)[Index]); | |
} | |
}; | |
struct vec { | |
float data[3]; | |
[[no_unique_address]] | |
Property<ArrayMemberGetter<vec, float, 3, &vec::data, 0>> x; | |
[[no_unique_address]] | |
Property<ArrayMemberGetter<vec, float, 3, &vec::data, 1>> y; | |
[[no_unique_address]] | |
Property<ArrayMemberGetter<vec, float, 3, &vec::data, 2>> z; | |
}; | |
CHECK_PROPERTY_OFFSET(vec, x); | |
CHECK_PROPERTY_OFFSET(vec, y); | |
CHECK_PROPERTY_OFFSET(vec, z); | |
int main() { | |
vec v = {0, 1, 2}; | |
return v.y; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment