C++ bindings for libpmemobj (part 1) - pmem resident variables
One of the biggest hurdles and error prone things about our C API is that the user has to manually keep track of modifications to the persistent memory resident variables while in a transaction. A special semi-transparent template property class has been implemented to automatically add variable modifications to the transaction undo log.
pmem::obj::p
Let’s start with the vector example from the previous tutorial series. It looked like this:
struct vector {
int x;
int y;
int z;
}
PMEMoid root = pmemobj_root(pop, sizeof (struct vector));
struct vector *vectorp = pmemobj_direct(root);
TX_BEGIN(pop) {
pmemobj_tx_add_range(root, 0, sizeof (struct vector));
vectorp->x = 5;
vectorp->y = 10;
vectorp->z = 15;
} TX_END
As you can see, the programmer has to remember to call pmemobj_tx_add_range
function before any modifications to the memory. In a simple case like this one
it might not be such a big deal, but once the code gets complex it may lead to
some difficult to find consistency issues.
By using the C++ API we can simplify this code like so:
#include <libpmemobj/p.hpp>
using namespace pmem::obj;
struct vector {
p<int> x;
p<int> y;
p<int> z;
}
PMEMoid root = pmemobj_root(pop, sizeof (struct vector));
struct vector *vectorp = pmemobj_direct(root);
TX_BEGIN(pop) {
vectorp->x = 5;
vectorp->y = 10;
vectorp->z = 15;
} TX_END
The template class pmem::obj::p
does not add storage overhead. The size of
the vector structure is exactly the same as in the C version.
This mechanism works overriding operator=
and adding the memory to the undo log
before modification. It’s pretty straightforward.