# rcp `rcp` is an intrusive reference-counting smart pointer for C++11(+). The reference count is stored inside the managed object itself, eliminating the separate heap allocation that non-intrusive implementations require. Memory is always freed when the last `rcp` to an object is destroyed. ## Usage Add `rcp_managed_root` to the root of your class hierarchy. For derived classes, add `rcp_managed`. ```cpp class Animal { rcp_managed_root(Animal); protected: Animal(std::string name) : name(std::move(name)) {} virtual ~Animal() {} public: std::string name; }; class Dog : public Animal { rcp_managed(Dog); protected: Dog(std::string name) : Animal(std::move(name)) {} public: void bark() { std::cout << "Woof!\n"; } }; ``` `rcp_managed_root` injects the reference count and the increment/decrement methods. `rcp_managed` injects `create()` and `get_rcp()` into derived classes. Both macros end with a `private:` access specifier, so follow them with `protected:` or `public:` as needed. > **Note:** If the class will be inherited, its destructor must be marked > `virtual`. > Without this, destroying a derived object through a base class pointer > will not call the derived destructor, leading to incomplete cleanup. ## Creating objects Use the static `create()` method, which returns an `rcp` holding the newly constructed object: ```cpp rcp animal = Animal::create("Cat"); rcp dog = Dog::create("Rex"); ``` `create()` forwards its arguments to the class constructor. Constructors should be `protected` or `private` to ensure objects are always managed by `rcp`. ## Wrapping external classes `rcp` can add intrusive reference counting to a class you don't own and cannot modify. Derive from the external class, add `rcp_managed_root` to the derived class, and give the derived class the same name inside your own namespace. Consumers use your type and never interact with the external class directly. ```cpp namespace external { class Counter { public: int start; int count; Counter(int start) : start(start), count(start) {} virtual ~Counter() {} }; } class Counter : public external::Counter { rcp_managed_root(Counter); protected: Counter(int start) : external::Counter(start) {} ~Counter() {} }; auto a = Counter::create(10); auto b = Counter::create(20); a->count++; // direct access to external::Counter members ``` The derived class inherits all members of the external class. `create()` forwards its arguments through to the external class constructor. The virtual destructor on the external class ensures that the derived destructor is called correctly when the last `rcp` is released. ## Basic usage `rcp` behaves like a raw pointer for access: ```cpp rcp dog = Dog::create("Rex"); dog->bark(); std::cout << (*dog).name << "\n"; if (dog) std::cout << "dog is valid\n"; dog.reset(); // releases the reference; object freed if this was the last holder ``` ## Transparent handles Defining a `typedef` (or `using`) of `rcp` as `X` lets consumers use `X` as if it were a plain value type, with no awareness of reference counting or implementation classes. ```cpp class ImageImpl { rcp_managed_root(ImageImpl); protected: ImageImpl(int width, int height) : width(width), height(height), pixels(width * height) {} ~ImageImpl() {} public: int width, height; std::vector pixels; }; typedef rcp Image; ``` Consumers work entirely with `Image` — creating, copying, and passing it like a value, while the pixel data is shared automatically and freed when no longer referenced. ```cpp Image load_thumbnail(Image full) { return full; } // shares pixel data Image icon = Image::create(16, 16); Image copy = icon; // shared ownership, no pixel copy Image thumb = load_thumbnail(icon); // still the same pixel data icon.reset(); copy.reset(); // pixel data freed here, when thumb (the last holder) goes out of scope ``` ## Copying and moving Copying an `rcp` increments the reference count. Moving transfers ownership without touching the reference count, and leaves the source null: ```cpp rcp a = Dog::create("Rex"); rcp b = a; // a and b share ownership; refcount = 2 rcp c = std::move(a); // c takes ownership from a; a is now null; refcount = 2 ``` ## Upcasting Assigning an `rcp` to an `rcp` is implicit. Attempting a downcast this way is a compile error: ```cpp rcp dog = Dog::create("Rex"); rcp animal = dog; // ok: implicit upcast rcp dog2 = animal; // error: implicit downcast not allowed ``` ## Downcasting Use `rcp_dynamic_cast` for explicit checked downcasts. It returns a null `rcp` if the object is not of the target type. Passing an rvalue transfers ownership on success and leaves the source unchanged on failure: ```cpp rcp animal = Dog::create("Rex"); // copy downcast: animal remains valid regardless of outcome rcp dog = rcp_dynamic_cast(animal); // move downcast: animal is nulled on success, left intact on failure rcp dog2 = rcp_dynamic_cast(std::move(animal)); ``` ## Comparison ```cpp rcp a = Dog::create("Rex"); rcp b = a; rcp c = Dog::create("Buddy"); assert(a == b); // same object, different pointer types: ok assert(a != c); assert(c != nullptr); ``` ## Getting an rcp from this Use `get_rcp()` inside a member function to obtain a reference-counted pointer to the current object: ```cpp class Dog : public Animal { rcp_managed(Dog); public: rcp self() { return get_rcp(); } }; ``` A managed class can also pass its raw `this` pointer directly to any function or class that accepts an `rcp`, and the existing reference count will be used — no separate control block is created. This is safe because the reference count is stored in the object itself. With `std::shared_ptr`, constructing from a raw `this` pointer creates an independent control block, leading to a double-free when both reach zero. ```cpp struct EventListener; static std::vector> g_listeners; struct EventListener { rcp_managed_root(EventListener); public: int events_received = 0; void attach() { g_listeners.push_back(this); } }; auto a = EventListener::create(); auto b = EventListener::create(); a->attach(); b->attach(); for (auto & l : g_listeners) l->events_received++; ``` ## Comparison with `std::shared_ptr` | | `rcp` | `std::shared_ptr` | |---|---|---| | 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`) | | Custom deleters | No | Yes | | Pointer to `this` | `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 allocation instead of two. The trade-off is that the class must be written with `rcp` in mind. `std::enable_shared_from_this` is tied to the type it is inherited from, so `shared_from_this()` always returns a `shared_ptr` to that base type. A derived class method that needs a `shared_ptr` to its own type must manually cast: ```cpp // shared_ptr: verbose and requires knowing the base std::shared_ptr self() { return std::dynamic_pointer_cast(shared_from_this()); } ``` With `rcp`, `rcp_managed` injects a `get_rcp()` that returns the correct derived type directly: ```cpp // rcp: returns rcp directly, no cast needed rcp self() { return get_rcp(); } ``` ## Comparison with `boost::intrusive_ptr` Both are intrusive reference-counting pointers, but they differ in how the reference counting is wired up: | | `rcp` | `boost::intrusive_ptr` | |---|---|---| | Setup | `rcp_managed_root` / `rcp_managed` macros | User-defined `intrusive_ptr_add_ref` / `intrusive_ptr_release` free functions and ref-count storage | | Upcasting | Implicit via constructor | Implicit via raw pointer conversion | | Checked downcast | `rcp_dynamic_cast` | Not built in | `rcp` automates the boilerplate of wiring up the reference count. `boost::intrusive_ptr` is more flexible — it can wrap types that already have their own reference counting (e.g. COM objects, OS handles) by implementing the free functions to call the existing mechanism.