Search code examples
c++c++11boostshared-memoryboost-interprocess

Allocating a user defined struct in shared memory with boost::interprocess


I am trying to use boost::interprocess to allocate a very simple data structure in shared memory but I cannot quite figure out how to use the boost interprocess allocators to perform the memory allocations/deallocations within the shared memory segment which I allocate as follows

using namespace boost::interprocess;
shared_memory_object::remove("MySharedMem");
mSharedMemory = std::make_unique<managed_shared_memory>(
    open_or_create, "MySharedMem", 65536);

I previously asked a similar question but unfortunately I never got any answers. MyStruct below is essentially an array with a length field indicating the size of the array. For now I have a simple length field but I will add some other constructor arguments later (bool's and other simple types).

In order to allocate this in the shared memory segment, I know I have to do something with allocators but I cannot find a similar example where I have a user defined type containing an array/pointer field.

    using MyType = struct MyType {
        explicit MyType(const size_t aSize)
            : mSize(aSize)
            , mpData(new char[aSize])
        {}

        ~MyType() {
            delete[]mpData;
        }
        size_t mSize;
        char * mpData;
    };

    using MyTypeAllocator = boost::interprocess::allocator<MyType,
        boost::interprocess::managed_shared_memory::segment_manager>;

    // Initialize the shared memory STL-compatible allocator
    MyTypeAllocator alloc(mSharedMemory->get_segment_manager());

Solution

  • Just don't do manual allocation. If you want a contiguous allocation of aSize elements of type char, that's what C++ has std::vector for.

    Best of all, std::vector already knows how to use another allocator, so there is really no reason not to use it:

    template <typename Alloc>
    struct MyType {
        explicit MyType(size_t aSize, Alloc alloc = {}) : mData(aSize, alloc) {}
    
      private:
        std::vector<char, Alloc> mData;
    };
    

    Now to play well with standard library construct/scoped allocators, you might want to define the allocator_type nested type:

        using allocator_type = Alloc; // typename Alloc::template rebind<char>::other;
    

    That's all. Just use it as any standard library type that has an allocator:

    int main() {
        using namespace Shared;
    
        Shared::remove("MySharedMem");
        auto memory = Segment(create_only, "MySharedMem", 65536);
    
        using A = Alloc<char>;
        A alloc(memory.get_segment_manager());
    
        auto* data = memory.find_or_construct<MyType<A>>("data")(1024, memory.get_segment_manager());
    
        return data? 0 : 255;
    }
    

    I created a few convenience typedefs in the Shared namespace, for maintainability. Here's the full sample

    Full Sample

    Live On Coliru ¹

    #include <boost/interprocess/managed_shared_memory.hpp>
    #include <vector>
    
    template <typename Alloc>
    struct MyType {
        using allocator_type = typename Alloc::template rebind<char>::other;
    
        explicit MyType(size_t aSize, Alloc alloc = {}) : mData(aSize, alloc) {}
    
      private:
        std::vector<char, Alloc> mData;
    };
    
    namespace Shared {
        namespace bip = boost::interprocess;
    
        using Segment = bip::managed_shared_memory;
        using Manager = Segment::segment_manager;
        template <typename T> 
            using Alloc = bip::allocator<T, Manager>;
    
        void remove(char const* name) { bip::shared_memory_object::remove(name); }
    
        using bip::create_only;
    }
    
    int main() {
        using namespace Shared;
    
        Shared::remove("MySharedMem");
        auto memory = Segment(create_only, "MySharedMem", 65536);
    
        using A = Alloc<char>;
        A alloc(memory.get_segment_manager());
    
        auto* data = memory.find_or_construct<MyType<A>>("data")(1024, memory.get_segment_manager());
    
        return data? 0 : 255;
    }
    

    ¹ For Coliru uses managed mapped file because shared memory is not supported there