Home | Libraries | People | FAQ | More |
When creating shared memory and memory mapped files to communicate two processes the memory segment can be mapped in a different address in each process:
#include<boost/interprocess/shared_memory_object.hpp> // ... using boost::interprocess; //Open a shared memory segment shared_memory_object shm_obj (open_only //open or create ,"shared_memory" //name ,read_only //read-only mode ); //Map the whole shared memory mapped_region region ( shm //Memory-mappable object , read_write //Access mode ); //This address can be different in each process void *addr = region.get_address();
This makes the creation of complex objects in mapped regions difficult: a C++ class instance placed in a mapped region might have a pointer pointing to another object also placed in the mapped region. Since the pointer stores an absolute address, that address is only valid for the process that placed the object there unless all processes map the mapped region in the same address.
To be able to simulate pointers in mapped regions, users must use offsets (distance between objects) instead of absolute
addresses. The offset between two objects in a mapped region is the same for
any process that maps the mapped region, even if that region is placed in different
base addresses. To facilitate the use of offsets, Boost.Interprocess
offers offset_ptr
.
offset_ptr
wraps
all the background operations needed to offer a pointer-like interface. The
class interface is inspired in Boost Smart Pointers and this smart pointer
stores the offset (distance in bytes) between the pointee's address and it's
own this
pointer. Imagine a structure
in a common 32 bit processor:
struct structure { int integer1; //The compiler places this at offset 0 in the structure offset_ptr<int> ptr; //The compiler places this at offset 4 in the structure int integer2; //The compiler places this at offset 8 in the structure }; //... structure s; //Assign the address of "integer1" to "ptr". //"ptr" will store internally "-4": // (char*)&s.integer1 - (char*)&s.ptr; s.ptr = &s.integer1; //Assign the address of "integer2" to "ptr". //"ptr" will store internally "4": // (char*)&s.integer2 - (char*)&s.ptr; s.ptr = &s.integer2;
One of the big problems of offset_ptr
is the representation of the null pointer. The null pointer can't be safely
represented like an offset, since the absolute address 0 is always outside
of the mapped region. Due to the fact that the segment can be mapped in a different
base address in each process the distance between the address 0 and offset_ptr
is different for every process.
Some implementations choose the offset 0 (that is, an offset_ptr
pointing to itself) as the null pointer pointer representation but this is
not valid for many use cases since many times structures like linked lists
or nodes from STL containers point to themselves (the end node in an empty
container, for example) and 0 offset value is needed. An alternative is to
store, in addition to the offset, a boolean to indicate if the pointer is null.
However, this increments the size of the pointer and hurts performance.
In consequence, offset_ptr
defines offset 1 as the null pointer, meaning that this class can't
point to the byte after its own this pointer:
using namespace boost::interprocess; offset_ptr<char> ptr; //Pointing to the next byte of it's own address //marks the smart pointer as null. ptr = (char*)&ptr + 1; //ptr is equal to null assert(!ptr); //This is the same as assigning the null value... ptr = 0; //ptr is also equal to null assert(!ptr);
In practice, this limitation is not important, since a user almost never wants to point to this address.
offset_ptr
offers
all pointer-like operations and random_access_iterator typedefs, so it can
be used in STL algorithms requiring random access iterators and detected via
traits. For more information about the members and operations of the class,
see offset_ptr reference
.