Skip to content

Instantly share code, notes, and snippets.

@strega-nil
Created April 9, 2020 19:33
Show Gist options
  • Save strega-nil/062915215ac629c214cd6b2163c0e3fd to your computer and use it in GitHub Desktop.
Save strega-nil/062915215ac629c214cd6b2163c0e3fd to your computer and use it in GitHub Desktop.
// 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