-
Notifications
You must be signed in to change notification settings - Fork 0
/
property.hpp
205 lines (166 loc) · 5.91 KB
/
property.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
//
// Copyright (c) Ho Han Kit Ivan. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for
// details.
//
#ifndef FCP_PROPERTY_HPP_
#define FCP_PROPERTY_HPP_
#include <cstddef>
#include <iosfwd>
namespace fcp {
namespace prop_detail {
template <typename T>
struct extract_memfn_types;
} // namespace prop_detail
template <typename T, auto GetFn, auto SetFn = nullptr>
class property;
template <typename T, auto GetFn, auto SetFn>
std::ostream &operator<<(std::ostream &os, const property<T, GetFn, SetFn> &p);
template <typename T, auto GetFn, auto SetFn>
class property {
private:
using GetFnInfo = prop_detail::extract_memfn_types<decltype(GetFn)>;
using SetFnInfo = prop_detail::extract_memfn_types<decltype(SetFn)>;
public:
explicit property() = default;
explicit property(const property &) = default;
explicit property(property &&) = default;
T get() const noexcept(GetFnInfo::is_noexcept_v);
void set(const T &val) noexcept(SetFnInfo::is_noexcept_v);
operator T() const noexcept(GetFnInfo::is_noexcept_v);
property &operator=(const T &rhs) noexcept(SetFnInfo::is_noexcept_v);
template <typename T2, auto GetFn2, auto SetFn2>
property &operator=(const property<T2, GetFn2, SetFn2> &rhs) noexcept(
SetFnInfo::is_noexcept_v &&noexcept(rhs.get()));
private:
using GetT = typename GetFnInfo::type;
using SetT = typename SetFnInfo::type;
int offset_;
const GetT *getter_this() const noexcept;
SetT *setter_this() noexcept;
};
template <typename T, auto GetFn>
class property<T, GetFn, nullptr> {
private:
using GetFnInfo = prop_detail::extract_memfn_types<decltype(GetFn)>;
public:
explicit property() = default;
explicit property(const property &) = default;
explicit property(property &&) = default;
T get() const noexcept(GetFnInfo::is_noexcept_v);
operator T() const noexcept(noexcept(get()));
private:
using GetT = typename prop_detail::extract_memfn_types<decltype(GetFn)>::type;
int offset_;
const GetT *getter_this() const noexcept;
};
} // namespace fcp
#define PROPERTIES_BEGIN() \
union { \
::fcp::prop_detail::property_offset PROPERTY_OFFSET{this};
#define PROPERTIES_END() \
} \
;
/* Implementation follows below. */
namespace fcp {
namespace prop_detail {
template <typename Mem, typename Ret, typename... Args>
struct extract_memfn_types<Ret (Mem::*)(Args...)> {
using type = Mem;
using ret = Ret;
static constexpr bool is_noexcept_v = false;
};
template <typename Mem, typename Ret, typename... Args>
struct extract_memfn_types<Ret (Mem::*)(Args...) const> {
using type = Mem;
using ret = Ret;
static constexpr bool is_noexcept_v = false;
};
template <typename Mem, typename Ret, typename... Args>
struct extract_memfn_types<Ret (Mem::*)(Args...) noexcept> {
using type = Mem;
using ret = Ret;
static constexpr bool is_noexcept_v = true;
};
template <typename Mem, typename Ret, typename... Args>
struct extract_memfn_types<Ret (Mem::*)(Args...) const noexcept> {
using type = Mem;
using ret = Ret;
static constexpr bool is_noexcept_v = true;
};
class property_offset {
public:
explicit property_offset(const void *obj) {
// rather than store the "this" pointer, we store the offset
// this allows us to copy/move the property_offset class trivially
// without invoking copy/move semantics
const auto member_ptr = reinterpret_cast<const std::byte *>(this);
const auto obj_ptr = reinterpret_cast<const std::byte *>(obj);
offset_ = member_ptr - obj_ptr;
}
private:
int offset_;
};
} // namespace prop_detail
/* Property implementation */
template <typename T, auto GetFn, auto SetFn>
T property<T, GetFn, SetFn>::get() const noexcept(GetFnInfo::is_noexcept_v) {
return (getter_this()->*GetFn)();
}
template <typename T, auto GetFn, auto SetFn>
property<T, GetFn, SetFn>::operator T() const
noexcept(GetFnInfo::is_noexcept_v) {
return get();
}
template <typename T, auto GetFn, auto SetFn>
void property<T, GetFn, SetFn>::set(const T &val) noexcept(
SetFnInfo::is_noexcept_v) {
(setter_this()->*SetFn)(val);
}
template <typename T, auto GetFn, auto SetFn>
property<T, GetFn, SetFn> &property<T, GetFn, SetFn>::operator=(
const T &val) noexcept(SetFnInfo::is_noexcept_v) {
set(val);
return *this;
}
template <typename T, auto GetFn, auto SetFn>
template <typename T2, auto GetFn2, auto SetFn2>
property<T, GetFn, SetFn> &property<T, GetFn, SetFn>::operator=(
const property<T2, GetFn2, SetFn2>
&rhs) noexcept(SetFnInfo::is_noexcept_v &&noexcept(rhs.get())) {
(setter_this()->*SetFn)(rhs.get());
return *this;
}
template <typename T, auto GetFn, auto SetFn>
const typename property<T, GetFn, SetFn>::GetT *
property<T, GetFn, SetFn>::getter_this() const noexcept {
const auto member_ptr = reinterpret_cast<const std::byte *>(this);
return reinterpret_cast<const GetT *>(member_ptr - offset_);
}
template <typename T, auto GetFn, auto SetFn>
typename property<T, GetFn, SetFn>::SetT *
property<T, GetFn, SetFn>::setter_this() noexcept {
const auto member_ptr = reinterpret_cast<std::byte *>(this);
return reinterpret_cast<SetT *>(member_ptr - offset_);
}
template <typename T, auto GetFn, auto SetFn>
std::ostream &operator<<(std::ostream &os, const property<T, GetFn, SetFn> &p) {
return os << p.get();
}
/* Implementation for property with no setter */
template <typename T, auto GetFn>
property<T, GetFn, nullptr>::operator T() const noexcept(noexcept(get())) {
return get();
}
template <typename T, auto GetFn>
T property<T, GetFn, nullptr>::get() const noexcept(GetFnInfo::is_noexcept_v) {
return (getter_this()->*GetFn)();
}
template <typename T, auto GetFn>
const typename property<T, GetFn, nullptr>::GetT *
property<T, GetFn, nullptr>::getter_this() const noexcept {
const auto member_ptr = reinterpret_cast<const std::byte *>(this);
return reinterpret_cast<const GetT *>(member_ptr - offset_);
}
} // namespace fcp
#endif