![]() |
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
.