Skip to content

Instantly share code, notes, and snippets.

@Nekotekina
Last active September 29, 2018 18:52
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 Nekotekina/84f333674c1fcd902115e0010d889745 to your computer and use it in GitHub Desktop.
Save Nekotekina/84f333674c1fcd902115e0010d889745 to your computer and use it in GitHub Desktop.
Property examples
// Helper type to examine pointers to members
template <typename MemPtrType>
struct memptr_traits
{
static_assert(std::is_member_pointer_v<MemPtrType>, "Not a member pointer");
};
// Pointer to data member specialization
template <typename Base, typename Type>
struct memptr_traits<Type Base::*>
{
using base = Base;
using type = Type;
using arg0 = Type;
};
// Setter member function specialization
template <typename Base, typename Type, typename Arg0, typename... Args>
struct memptr_traits<Type(Base::*)(Arg0, Args...)>
{
using base = Base;
using type = Type;
using arg0 = Arg0;
};
// Getter member function specialization
template <typename Base, typename Type>
struct memptr_traits<Type(Base::*)() const>
{
using base = Base;
using type = Type;
};
// Property with getter and optionally setter specified as pointers to members (data or functions)
template <auto Get, auto Set = 0>
class property
{
using outer = typename memptr_traits<decltype(Get)>::base;
using inner = typename memptr_traits<decltype(Get)>::type;
outer& m_this;
friend outer;
// Cannot have a copy constructor
property(const property&) = delete;
// Copy assignment does nothing
property& operator=(const property&) noexcept
{
return *this;
}
public:
// Outer class should initialize each property with `this` pointer
constexpr property(outer* _this) noexcept
: m_this(*_this)
{
}
// Invoke getter
operator inner() const
{
return std::invoke(Get, m_this);
}
// Invoke getter
inner get() const
{
return std::invoke(Get, m_this);
}
// Invoke setter, forward return value
decltype(auto) operator=(inner value)
{
if constexpr (std::is_member_object_pointer_v<decltype(Set)>)
{
(m_this.*Set) = std::move(value);
return *this;
}
else
{
return std::invoke(Set, m_this, std::move(value));
}
}
};
// Read-only property specialization
template <auto Get>
class property<Get, 0>
{
using outer = typename memptr_traits<decltype(Get)>::base;
using inner = typename memptr_traits<decltype(Get)>::type;
outer& m_this;
friend outer;
// Cannot have a copy constructor
property(const property&) = delete;
// Copy assignment does nothing
property& operator=(const property&) noexcept
{
return *this;
}
public:
// Outer class should initialize each property with `this` pointer
constexpr property(outer* _this) noexcept
: m_this(*_this)
{
}
// Invoke getter
operator inner() const
{
return std::invoke(Get, m_this);
}
// Invoke getter
inner get() const
{
return std::invoke(Get, m_this);
}
};
// Write-only property specialization
template <auto Set>
class property<0, Set>
{
using outer = typename memptr_traits<decltype(Set)>::base;
using inner = typename memptr_traits<decltype(Set)>::arg0;
outer& m_this;
friend outer;
// Cannot have a copy constructor
property(const property&) = delete;
// Copy assignment does nothing
property& operator=(const property&) noexcept
{
return *this;
}
public:
// Outer class should initialize each property with `this` pointer
constexpr property(outer* _this) noexcept
: m_this(*_this)
{
}
// Invoke setter, forward return value
decltype(auto) operator=(inner value)
{
if constexpr (std::is_member_object_pointer_v<decltype(Set)>)
{
(m_this.*Set) = std::move(value);
return void();
}
else
{
return std::invoke(Set, m_this, std::move(value));
}
}
};
// Helper type to examine pointers to members (omitted)
template <typename MemPtrType>
struct memptr_traits;
// Property with getter and optionally setter specified as pointers to members (data or functions)
template <auto Get, auto Set, auto Offset>
class property
{
using outer = typename memptr_traits<decltype(Get)>::base;
using inner = typename memptr_traits<decltype(Get)>::type;
// Hidden copy assignment operator
property& operator=(const property&) noexcept = default;
const outer* hack_this() const
{
return reinterpret_cast<const outer*>(reinterpret_cast<const char*>(this) - Offset());
}
outer* hack_this()
{
return reinterpret_cast<outer*>(reinterpret_cast<char*>(this) - Offset());
}
friend outer;
public:
// Invoke getter
operator inner() const
{
return std::invoke(Get, *hack_this());
}
// Invoke getter
inner get() const
{
return std::invoke(Get, *hack_this());
}
// Invoke setter, forward return value
decltype(auto) operator=(inner value)
{
if constexpr (std::is_member_object_pointer_v<decltype(Set)>)
{
(hack_this()->*Set) = std::move(value);
return *this;
}
else
{
return std::invoke(Set, *hack_this(), std::move(value));
}
}
};
struct prop_test
{
int m_x;
static inline std::ptrdiff_t off_x()
{
return offsetof(prop_test, x);
}
[[no_unique_address]] property<&prop_test::m_x, &prop_test::m_x, &off_x> x;
};
// Helper type to examine pointers to members (omitted)
template <typename MemPtrType>
struct memptr_traits;
namespace std
{
// Helper for extracting outer class from the pointer to member
template <auto MemPtrType>
using outer_t = typename memptr_traits<decltype(MemPtrType)>::base;
}
// Property with getter and optionally setter specified as pointers to members (data or functions)
template <auto Get, auto Set = 0, this auto::* Self>
class property
{
using inner = typename memptr_traits<decltype(Get)>::type;
// Hidden copy assignment operator
property& operator=(const property&) noexcept = default;
friend std::outer_t<Self>;
public:
// Invoke getter
operator inner() const
{
return std::invoke(Get, *static_cast<const std::outer_t<Self>*>(this));
}
// Invoke getter
inner get() const
{
return std::invoke(Get, *static_cast<const std::outer_t<Self>*>(this));
}
// Invoke setter, forward return value
decltype(auto) operator=(inner value)
{
if constexpr (std::is_member_object_pointer_v<decltype(Set)>)
{
(static_cast<std::outer_t<Self>*>(this)->*Set) = std::move(value);
return *this;
}
else
{
return std::invoke(Set, *static_cast<std::outer_t<Self>*>(this), std::move(value));
}
}
};
// Read-only property specialization
template <auto Get, this auto::* Self>
class property<Get, 0, Self>
{
using inner = typename memptr_traits<decltype(Get)>::type;
// Hidden copy assignment operator
property& operator=(const property&) noexcept = default;
friend std::outer_t<Self>;
public:
// Invoke getter
operator inner() const
{
return std::invoke(Get, *static_cast<const std::outer_t<Self>*>(this));
}
// Invoke getter
inner get() const
{
return std::invoke(Get, *static_cast<const std::outer_t<Self>*>(this));
}
};
// Write-only property specialization
template <auto Set, this auto::* Self>
class property<0, Set, Self>
{
using inner = typename memptr_traits<decltype(Set)>::arg0;
property& operator=(const property&) noexcept = default;
friend std::outer_t<Self>;
public:
// Invoke setter, forward return value
decltype(auto) operator=(inner value)
{
if constexpr (std::is_member_object_pointer_v<decltype(Set)>)
{
(static_cast<std::outer_t<Self>*>(this)->*Set) = std::move(value);
return void();
}
else
{
return std::invoke(Set, *static_cast<std::outer_t<Self>*>(this), std::move(value));
}
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment