C++ bindings for libpmemobj (part 2) - persistent smart pointer
In our C API the programmer has to deal with custom pointers represented by the PMEMoid structure. Thanks to some macro magic we made it so that those PMEMoids are somewhat usable. C++ allows us to evolve this concept.
pmem::obj::persistent_ptr
Almost everyone who ever touched a C++ code knows the idea behind smart pointers
(for example, std::shared_ptr
). Our persistent pointer works in the same way.
It wraps around a type and provides implementation of operator*
, operator->
and operator[]
.
A constructor from raw PMEMoid is provided, so that mixing the C API with C++ is possible.
As always, we are going to start with an example:
#include <libpmemobj/p.hpp>
#include <libpmemobj/persistent_ptr.hpp>
using namespace pmem::obj;
struct rectangle {
p<int> a;
p<int> b;
};
struct root {
persistent_ptr<rectangle> rect;
};
It’s a modified rectangle example from transactional allocations tutorial. Layout declaration using macros is no longer required :)
As I previously said, the persistent pointers can be constructed from PMEMoids, and as such, we are going to allocate the rectangle by using the regular C API.
persistent_ptr<root> rootp = pmemobj_root(pop, sizeof (root));
TX_BEGIN(pop) {
persistent_ptr<rectangle> rect = pmemobj_tx_alloc(sizeof (rectangle), 0);
rect->x = 5;
rect->y = 10;
rootp->rect = rect; /* assignments are automatically added to TX */
} TX_END
As you can see, pretty easy. No more ugly D_RW
or D_RO
macros ! :)
There’s one thing to highlight here: The rectangle constructor is NOT called in this example. This is because we are using C allocation function. This is equivalent to a following construct in a regular C++:
shared_ptr<rectangle> rect((rectangle *)malloc(sizeof(rectangle)));
To free a persistent_ptr
using the C API, a special raw()
function is available
that returns a const reference to the PMEMoid.
TX_BEGIN(pop) {
pmemobj_tx_free(rootp->rect.raw());
rootp->rect = nullptr;
} TX_END
Later tutorials will introduce proper allocator functions that do call the constructor and destructors accordingly.
The persistent_ptr
class also implements a raw_ptr()
function which returns
a pointer to the PMEMoid - this enables usage of the C failsafe atomic API.
Right now the persistent_ptr class can only be used with non-polymorphic and trivially default constructible classes. Those limitations might be relaxed in later versions of the bindings.