C++ bindings for libpmemobj (part 2) - persistent smart pointer

Posted January 12, 2016         « Previous post     Next post »

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.

nvml::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:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <libpmemobj/p.hpp>
#include <libpmemobj/persistent_ptr.hpp>

using namespace nvml::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.

1
2
3
4
5
6
7
8
9
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++:

1
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.

1
2
3
4
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.



Posted by @pbalcer         « Previous post     Next post »