Compare commits

...
4 Commits
3 changed files with 39 additions and 6 deletions

No files matched your search

+9 -1
View File
@@ -319,6 +319,7 @@ for (auto & l : g_listeners) l->events_received++;
| | `rcp` | `std::shared_ptr` |
|---|---|---|
| Pointer size | One pointer | Two pointers (object + control block) |
| Reference count location | Inside the object | Separate control block (extra allocation) |
| Class requirement | `rcp_managed_root` / `rcp_managed` macro | None (any type) |
| Weak pointers | No | Yes (`std::weak_ptr`) |
@@ -326,7 +327,14 @@ for (auto & l : g_listeners) l->events_received++;
| Pointer to `this` | implicit conversion from `this` or explicit `get_rcp()` | `std::enable_shared_from_this` |
| Checked downcast | `rcp_dynamic_cast` | `std::dynamic_pointer_cast` |
The intrusive design means each managed object requires only one heap
Because `rcp` stores only a single raw pointer, `sizeof(rcp<T>)` equals
`sizeof(T *)` — typically 8 bytes on a 64-bit platform.
`std::shared_ptr` stores both a pointer to the object and a pointer to the
control block, so `sizeof(std::shared_ptr<T>)` is typically 16 bytes.
This makes `rcp` more efficient to pass by value, copy, and store in
containers.
The intrusive design also means each managed object requires only one heap
allocation instead of two which can improve performance for many applications.
The trade-off is that the class must be written with `rcp` in mind.
+8 -5
View File
@@ -28,6 +28,9 @@
/**
* Reference-counting pointer.
*
* See https://github.com/holtrop/rcp for documentation, issues, and pull
* requests.
*/
template <typename T>
class rcp
@@ -276,23 +279,23 @@ rcp<T> rcp_dynamic_cast(rcp<U> && other)
#define rcp_managed_root(classname) \
private: \
mutable std::atomic<int> ref_count{0}; \
mutable std::atomic<int> rcp_ref_count{0}; \
void rcp_inc() const \
{ \
ref_count.fetch_add(1, std::memory_order_relaxed); \
rcp_ref_count.fetch_add(1, std::memory_order_relaxed); \
} \
void rcp_dec() const \
{ \
if (ref_count.fetch_sub(1, std::memory_order_acq_rel) == 1) \
if (rcp_ref_count.fetch_sub(1, std::memory_order_acq_rel) == 1) \
{ \
delete this; \
} \
} \
int rcp_count() const \
{ \
return ref_count.load(std::memory_order_relaxed); \
return rcp_ref_count.load(std::memory_order_relaxed); \
} \
template <typename T> friend class rcp; \
template <typename RCP_T_> friend class rcp; \
rcp_managed(classname)
#define rcp_managed(classname) \
+22
View File
@@ -413,6 +413,27 @@ void test_hash()
assert(map.find(c) == map.end());
}
template <typename T>
struct Box
{
rcp_managed_root(Box);
protected:
Box(T v) : v(v) {}
public:
T v;
};
void test_class_template()
{
auto a = Box<int>::create(42);
auto b = Box<int>::create(99);
assert(a->v == 42);
assert(b->v == 99);
auto c = a;
assert(c->v == 42);
assert(c.use_count() == 2);
}
void test_external_class()
{
int before = external_destruct;
@@ -454,6 +475,7 @@ int main(int argc, char * argv[])
test_use_count();
test_swap();
test_hash();
test_class_template();
test_external_class();
return 0;
}