smart_holder_poc.h 6.28 KB
Newer Older
1
2
3
#pragma once

#include <memory>
4
5
#include <stdexcept>
#include <string>
6
7
#include <typeinfo>

8
9
namespace pybindit {
namespace memory {
10

11
12
13
template <typename T>
struct guarded_builtin_delete {
  bool* flag_ptr;
14
15
  explicit guarded_builtin_delete(bool* guard_flag_ptr)
      : flag_ptr{guard_flag_ptr} {}
16
17
18
19
20
21
22
23
  void operator()(T* raw_ptr) {
    if (*flag_ptr) delete raw_ptr;
  }
};

template <typename T, typename D>
struct guarded_custom_deleter {
  bool* flag_ptr;
24
25
  explicit guarded_custom_deleter(bool* guard_flag_ptr)
      : flag_ptr{guard_flag_ptr} {}
26
27
28
29
30
  void operator()(T* raw_ptr) {
    if (*flag_ptr) D()(raw_ptr);
  }
};

31
32
33
struct smart_holder {
  const std::type_info* rtti_held;
  const std::type_info* rtti_uqp_del;
34
35
  std::shared_ptr<void> vptr;
  bool vptr_deleter_guard_flag;
36
37
38
  bool vptr_is_using_noop_deleter : 1;
  bool vptr_is_using_builtin_delete : 1;
  bool vptr_is_external_shared_ptr : 1;
39

40
41
42
  void clear() {
    rtti_held = nullptr;
    rtti_uqp_del = nullptr;
43
44
    vptr.reset();
    vptr_deleter_guard_flag = false;
45
    vptr_is_using_noop_deleter = false;
46
    vptr_is_using_builtin_delete = false;
47
    vptr_is_external_shared_ptr = false;
48
  }
49
50

  smart_holder()
51
52
      : rtti_held{nullptr},
        rtti_uqp_del{nullptr},
53
        vptr_deleter_guard_flag{false},
54
55
56
        vptr_is_using_noop_deleter{false},
        vptr_is_using_builtin_delete{false},
        vptr_is_external_shared_ptr{false} {}
57

58
59
  bool has_pointee() const { return vptr.get() != nullptr; }

60
  template <typename T>
61
  void ensure_compatible_rtti_held(const char* context) const {
62
63
64
65
    if (!rtti_held) {
      throw std::runtime_error(std::string("Unpopulated holder (") + context +
                               ").");
    }
66
67
    const std::type_info* rtti_requested = &typeid(T);
    if (!(*rtti_requested == *rtti_held)) {
68
      throw std::runtime_error(std::string("Incompatible type (") + context +
69
70
71
72
                               ").");
    }
  }

73
  template <typename D>
74
  void ensure_compatible_rtti_uqp_del(const char* context) const {
75
76
77
78
    if (!rtti_uqp_del) {
      throw std::runtime_error(std::string("Missing unique_ptr deleter (") +
                               context + ").");
    }
79
80
81
82
    const std::type_info* rtti_requested = &typeid(D);
    if (!(*rtti_requested == *rtti_uqp_del)) {
      throw std::runtime_error(
          std::string("Incompatible unique_ptr deleter (") + context + ").");
83
84
85
    }
  }

86
87
88
89
90
91
92
  void ensure_has_pointee(const char* context) const {
    if (!has_pointee()) {
      throw std::runtime_error(std::string("Disowned holder (") + context +
                               ").");
    }
  }

93
  void ensure_vptr_is_using_builtin_delete(const char* context) const {
94
95
96
97
98
99
100
101
    if (vptr_is_external_shared_ptr) {
      throw std::runtime_error(
          std::string("Cannot disown external shared_ptr (") + context + ").");
    }
    if (vptr_is_using_noop_deleter) {
      throw std::runtime_error(
          std::string("Cannot disown non-owning holder (") + context + ").");
    }
102
103
    if (!vptr_is_using_builtin_delete) {
      throw std::runtime_error(std::string("Cannot disown custom deleter (") +
104
                               context + ").");
105
106
107
    }
  }

108
  void ensure_use_count_1(const char* context) const {
109
110
111
112
113
114
    if (vptr.use_count() != 1) {
      throw std::runtime_error(std::string("Cannot disown use_count != 1 (") +
                               context + ").");
    }
  }

115
116
117
118
  template <typename T>
  void from_raw_ptr_unowned(T* raw_ptr) {
    clear();
    rtti_held = &typeid(T);
119
    vptr_is_using_noop_deleter = true;
120
121
122
123
124
125
126
127
128
129
    vptr.reset(raw_ptr, guarded_builtin_delete<T>(&vptr_deleter_guard_flag));
  }

  template <typename T>
  T* as_raw_ptr_unowned() const {
    static const char* context = "as_raw_ptr_unowned";
    ensure_compatible_rtti_held<T>(context);
    return static_cast<T*>(vptr.get());
  }

130
  template <typename T>
131
132
133
134
135
136
137
138
139
  const T& const_value_ref() const {
    static const char* context = "const_value_ref";
    ensure_compatible_rtti_held<T>(context);
    ensure_has_pointee(context);
    return *static_cast<T*>(vptr.get());
  }

  template <typename T>
  void from_raw_ptr_take_ownership(T* raw_ptr) {
140
141
    clear();
    rtti_held = &typeid(T);
142
    vptr_deleter_guard_flag = true;
143
    vptr_is_using_builtin_delete = true;
144
    vptr.reset(raw_ptr, guarded_builtin_delete<T>(&vptr_deleter_guard_flag));
145
146
  }

147
  template <typename T>
148
149
  T* as_raw_ptr_release_ownership(
      const char* context = "as_raw_ptr_release_ownership") {
150
    ensure_compatible_rtti_held<T>(context);
151
    ensure_vptr_is_using_builtin_delete(context);
152
153
    ensure_use_count_1(context);
    T* raw_ptr = static_cast<T*>(vptr.get());
154
    vptr_deleter_guard_flag = false;
155
156
    vptr.reset();
    return raw_ptr;
157
158
  }

159
160
161
162
  template <typename T>
  void from_unique_ptr(std::unique_ptr<T>&& unq_ptr) {
    clear();
    rtti_held = &typeid(T);
163
    vptr_deleter_guard_flag = true;
164
    vptr_is_using_builtin_delete = true;
165
166
    vptr.reset(unq_ptr.get(),
               guarded_builtin_delete<T>(&vptr_deleter_guard_flag));
167
168
169
170
171
    unq_ptr.release();
  }

  template <typename T>
  std::unique_ptr<T> as_unique_ptr() {
172
    return std::unique_ptr<T>(as_raw_ptr_release_ownership<T>("as_unique_ptr"));
173
174
175
176
177
178
179
  }

  template <typename T, typename D>
  void from_unique_ptr_with_deleter(std::unique_ptr<T, D>&& unq_ptr) {
    clear();
    rtti_held = &typeid(T);
    rtti_uqp_del = &typeid(D);
180
181
182
    vptr_deleter_guard_flag = true;
    vptr.reset(unq_ptr.get(),
               guarded_custom_deleter<T, D>(&vptr_deleter_guard_flag));
183
184
185
186
187
188
189
190
191
192
    unq_ptr.release();
  }

  template <typename T, typename D>
  std::unique_ptr<T, D> as_unique_ptr_with_deleter() {
    static const char* context = "as_unique_ptr_with_deleter";
    ensure_compatible_rtti_held<T>(context);
    ensure_compatible_rtti_uqp_del<D>(context);
    ensure_use_count_1(context);
    T* raw_ptr = static_cast<T*>(vptr.get());
193
    vptr_deleter_guard_flag = false;
194
195
    vptr.reset();
    return std::unique_ptr<T, D>(raw_ptr);
196
  }
197
198
199
200
201

  template <typename T>
  void from_shared_ptr(std::shared_ptr<T> shd_ptr) {
    clear();
    rtti_held = &typeid(T);
202
    vptr_is_external_shared_ptr = true;
203
204
205
206
    vptr = std::static_pointer_cast<void>(shd_ptr);
  }

  template <typename T>
207
  std::shared_ptr<T> as_shared_ptr() const {
208
209
210
211
    static const char* context = "as_shared_ptr";
    ensure_compatible_rtti_held<T>(context);
    return std::static_pointer_cast<T>(vptr);
  }
212
213
};

214
215
}  // namespace memory
}  // namespace pybindit