Last active
September 29, 2018 18:52
-
-
Save Nekotekina/84f333674c1fcd902115e0010d889745 to your computer and use it in GitHub Desktop.
Property examples
This file contains 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
// 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)); | |
} | |
} | |
}; |
This file contains 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
// 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; | |
}; |
This file contains 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
// 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