# rcp `rcp` is an intrusive reference-counting smart pointer for C++11(+). In contrast to smart pointer implementations that use a separately allocated control block with reference count (e.g. `std::shared_ptr`), the reference count is stored inside the managed object itself, eliminating the separate heap allocation that non-intrusive implementations require. The managed object is automatically destructed when the last `rcp` pointing to the object is destroyed. Importantly, since the reference count is stored within the managed object, this gives the object the freedom to pass its `this` pointer to any other object or function which takes a `rcp` managed pointer and a new `rcp` managed pointer can be automatically created from `this` to point to the same reference count. This is in contrast to `std::shared_ptr` or other smart pointer implementations which store the reference count externally to the managed object, where constructing another managed pointer from a raw `this` pointer can lead to a double free situation with two managed pointers pointing at the same object. The tradeoff is that the class definition must be modified to declare support for being managed by an `rcp` smart pointer. This does not mean, however, that class definitions which cannot be modified cannot be managed by `rcp` smart pointers. See the [Wrapping External Classes](#wrapping-external-classes) section for an explanation of how to do this. `rcp` is MIT-licensed. See [https://github.com/holtrop/rcp](https://github.com/holtrop/rcp) for issues, releases, and pull requests. ## Installation No installation is required to use `rcp`. `rcp` is entirely implemented within one header file `rcp.h`. Simply copy the header file (`rcp.h`) into your project or add the `rcp` repository as a submodule and add the `include` directory to your build system include path. ## 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"; // When `dog` goes out of scope and its destructor is called, it will decrement // the reference count of the `Dog` instance. If this was the last reference // to the object, the `Dog` instance will be deleted. Calling `reset()` on an // instance of a `rcp` managed pointer will also decrement the reference count // of the object currently being pointed to and then reset the reference to a // null reference. dog.reset(); ``` ## Defining Classes With rcp Support Example: ```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"; } }; ``` Add a call to the `rcp_managed_root` macro to the root of your class hierarchy. The argument to the macro is the name of the class. In a class hierarchy, there should be only one `rcp_managed_root` call at the highest managed level. Use `rcp_managed` for any classes deriving from a class using `rcp_managed_root`. `rcp_managed_root` injects the reference count and the increment/decrement methods. It also calls `rcp_managed` internally. `rcp_managed` injects `create()` and `get_rcp()` into derived classes. In this way, there will be only one reference count variable and one set of increment/decrement functions defined in any object. In contrast, the `get_rcp()` function will be defined within each class in the hierarchy so that it returns a reference to the specific type that it is called on. 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 The managed object's constructor should generally be declared with `protected` visibility instead of `public` visibility. This prevents a user from constructing an object instance without a managed pointer to it. The `rcp_managed` macro defines a public `create()` static function which will forward all arguments to the constructor and then call `get_rcp()` on the resulting object to create the initial managed pointer to it. In addition to the class itself defining a `create()` function, the `rcp` managed pointer class also declares a `create()` function which can be used to create an instance of the managed object. See [Transparent Handles](#transparent-handles) for a way this can be used. Users use the static `create()` method, which returns an `rcp` holding a managed pointer to the newly constructed object: ```cpp rcp animal = Animal::create("Cat"); rcp dog = Dog::create("Rex"); ``` ## Wrapping External Classes `rcp` can add intrusive reference counting to a class you don't own and cannot modify. Derive from the external class and add `rcp_managed_root` to the derived class. You can give the derived class the same name inside your own namespace or a different name if desired. Consumers use your type and never interact with the external class directly. Example: ```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. ## 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. Example: ```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 ``` This usage pattern is supported by `rcp` but is completely optional. This usage pattern provides a more concise syntax, however it could be less obvious to the user what is happening under the hood so it is entirely up to the user whether or not to use this pattern. ## Copying and Moving Copying an `rcp` increments the reference count. Moving efficiently 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 method 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`, in contrast, constructing from a raw `this` pointer creates an independent control block, leading to a double-free when both reach zero. Example: ```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` | 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 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. Note that `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` can be more flexible for some uses cases because 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.