Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Structures and vectors in Boost Shared Memory

I am new to Boost. I have the following structure and want to store it in shared memory using Boost.

struct InData{
    int x,y,h,w;
    char* lbl;
};

In-turn this structure will be stored in Vector. Most of example talk about int or string datatype for Vectors. I would like if anbody can provide an example how to store user defined data type into boost shared memory.

like image 396
Mrutyunjay Avatar asked Nov 04 '15 03:11

Mrutyunjay


1 Answers

You can easily store UDT in there, Boost's interprocess allocator will do the magic for you. However, storing raw-pointers is not gonna work.

So, let's start off with a sample without raw pointers:

struct InData {
    int x = 0, y = 0, h = 0, w = 0;
    shared_string label;
};

Assuming shared_string is already define like e.g.

using shared_string = bip::basic_string<char, std::char_traits<char>, bip::allocator<char, SegmentManager>>;

I like to separate my datastructures from the choice of allocator, so I might write it more generically:

template <typename Alloc = std::allocator<char> >
struct BasicInData {
    BasicInData(Alloc alloc = {}) : label(alloc) { }

    int x = 0, y = 0, h = 0, w = 0;
    string label;
};

Now, you can "freely" add this to a shared memory/memory mapped segment. I usually set up some typedefs in a namespace:

using InData = BasicInData<>; // just heap allocated

namespace Shared {
    using segment                      = bip::managed_mapped_file; // or managed_shared_memory
    using segment_manager              = segment::segment_manager;

    template <typename T> using alloc  = bip::allocator<T, segment_manager>;
    template <typename T> using vector = bip::vector<T, alloc<T> >;

    using InData = BasicInData<alloc<char> >; // shared memory version 
}

Now you can use the vector<InData> from Shared in a managed memory segment:

segment smt(bip::open_or_create, "data.bin", 10u<<20); // 10 MiB
vector<InData>* v = smt.find_or_construct<vector<InData> >("InDataVector")(smt.get_segment_manager());

This creates a vector inside a file of 10 MiB.

Full Demo Live

I've extended the demo with some functions to generate random data. The program looks like:

int main() {
    Shared::segment smt(bip::open_or_create, "data.bin", 10u<<20); // 10 MiB
    auto& data = Shared::locate(smt);

    std::generate_n(std::back_inserter(data), 2, [&smt] { return generate(smt.get_segment_manager()); });

    for(auto& d : data) {
        std::cout << d << "\n";
    }
}

Each time it's run it will add two randomly generated InData structures. First time output could be e.g.

InData { 99, 7, 71, 65, nwlsovjiwv }
InData { 16, 51, 33, 34, nuujiblavs }

Then, second time will show the existing records plus two newly generated lines:

InData { 99, 7, 71, 65, nwlsovjiwv }
InData { 16, 51, 33, 34, nuujiblavs }
InData { 49, 26, 81, 30, snhcvholti }
InData { 48, 66, 19, 8, xtididuegs }

Live On Coliru

#include <boost/interprocess/managed_shared_memory.hpp>

#include <boost/interprocess/managed_mapped_file.hpp> // use for Coliru
#include <boost/interprocess/containers/vector.hpp>   // boost/containers/vector.hpp
#include <boost/interprocess/containers/string.hpp>   // boost/containers/string.hpp
#include <iostream>
#include <random>

namespace bip = boost::interprocess;

template <typename Alloc = std::allocator<char> >
struct BasicInData {
    using string = bip::basic_string<char, std::char_traits<char>, typename Alloc::template rebind<char>::other>;

    BasicInData(Alloc alloc = {}) :
        label(alloc) 
    { }

    template <typename T>
    BasicInData(int x, int y, int h, int w, T&& label, Alloc alloc = {}) :
        x(x), y(y), h(h), w(w), label(std::forward<T>(label), alloc) 
    { }

    int x = 0, y = 0, h = 0, w = 0;
    string label;
};

using InData = BasicInData<>; // just heap allocated

namespace Shared {
    using segment                      = bip::managed_mapped_file;           // or managed_shared_memory
    using segment_manager              = segment::segment_manager;

    template <typename T> using alloc  = bip::allocator<T, segment_manager>;
    template <typename T> using vector = bip::vector<T, alloc<T> >;

    using InData = BasicInData<alloc<char> >; // shared memory version 

    vector<InData>& locate(segment& smt) {
        auto* v = smt.find_or_construct<vector<InData> >("InDataVector")(smt.get_segment_manager());
        assert(v);
        return *v;
    }
}

static std::mt19937 prng { std::random_device{} () };

// ugly quick and dirty data generator
template <typename SegmentManager>
Shared::InData generate(SegmentManager const& sm) {
    static std::uniform_int_distribution<int> coord_dist(1,100);
    static std::uniform_int_distribution<char> char_dist('a', 'z');
    char buf[11] = { 0 };

    auto rand_coord = [] { return coord_dist(prng); };
    auto gen_ch     = [] { return char_dist(prng); };
    auto rand_label = [&] { std::generate(std::begin(buf), std::end(buf)-1, gen_ch); return buf; };

    return { rand_coord(), rand_coord(), rand_coord(), rand_coord(), rand_label(), sm }; 
}

// demo:

template <typename Alloc>
static std::ostream& operator<<(std::ostream& os, BasicInData<Alloc> const& d) {
    return os << "InData { " << d.x << ", " << d.y << ", " <<
                                d.w << ", " << d.h << ", " << d.label << " }";
}

int main() {
    Shared::segment smt(bip::open_or_create, "data.bin", 10u<<10); // 10 Kb for coliru
    auto& data = Shared::locate(smt);

    std::generate_n(std::back_inserter(data), 2, [&smt] { return generate(smt.get_segment_manager()); });

    for(auto& d : data) {
        std::cout << d << "\n";
    }
}
like image 147
sehe Avatar answered Oct 08 '22 12:10

sehe