c++ boost icl containers in shared memory

2019-05-29 05:07发布

问题:

Im working with boost::icl::interval_map that works perfectly but i want this container to be stored in shared memory. Does boost provide support for storing boost::icl containers in shared memory

    using namespace std;
    using namespace boost::icl;

    struct IFM {
           std::string destinationGroup;
           int priority;
           IFM()
           {
               destinationGroup= "";
               priority = 0;

           }

           IFM(const std::string& d, const int& p)
                 {
                     destinationGroup= d;

                     priority = p;

                 }


           IFM& operator +=(const IFM& right)
            {
                destinationGroup+= right.destinationGroup;
                priority += right.priority;
                return *this;
            }


        bool operator <(const IFM& left) const {
            if(priority <  left.priority)
                return true;
        }

        bool operator ==(const IFM& left) const {
            return destinationGroup== left.destinationGroup
                    && priority == left.priority;

        }

    };

    typedef std::set<IFM> guests;


    void boost_party()
    {
        interval_map<double, guests> party;

        IFM i = {"123", 1};
        IFM j = {"124", 1};
        IFM k = {"126", 2,};
        IFM l = {"128", 1};
        IFM m = {"129", 1};
        IFM n = {"130", 1};

        guests ii;
        ii.insert(i);

        guests jj;
        jj.insert(j);

        guests kk;
        kk.insert(k);

        guests ll;
        ll.insert(l);


        guests mm;
        mm.insert(m);

        party.add(make_pair(interval<double>::closed(12345600000,12345699999), guests(ii)));
        party.add(make_pair(interval<double>::closed(32100000000,32199999999), guests(jj)));
        party.add(make_pair(interval<double>::closed(42000000000,42999999999), guests(ll)));
        party.add(make_pair(interval<double>::closed(42101000000,42101099999), guests(kk)));
        party.add(make_pair(interval<double>::closed(67000000000,67999999999), guests(mm)));

        interval_map<double, guests>::const_iterator it;
        it = party.find(42101035898);

        if (it != party.end()) {
            interval<double>::type when = it->first;
            guests who = (*it++).second;
            cout << who.size() << endl;
            for (auto it2 : who) {
                    cout << when << ": " << it2.destinationGroup<< endl;
            }
        }
    }

int main() {
    boost_party();
    return 0;
}

Gives me following output which is expected Now i m trying to put a simple map interval_map in shared memory first but my code never compiles

boost::interprocess::managed_shared_memory segment(
     boost::interprocess::create_only, "MySharedMemory" //segment name
     , 65536);

     typedef int KeyType;
     typedef int MappedType;
     typedef pair<int,int> keyvalue;

     typedef boost::interprocess::allocator<keyvalue, boost::interprocess::managed_shared_memory::segment_manager>
     ShmemAllocator;

     ShmemAllocator alloc_inst (segment.get_segment_manager());

     typedef boost::icl::interval_map<int, int, boost::icl::partial_absorber, std::less, boost::icl::inplace_plus,boost::icl::inter_section, boost::icl::discrete_interval<int, std::less>, ShmemAllocator> MyMap;

Give following error

error: type/value mismatch at argument 8 in template parameter list for ‘template class Compare, template class Combine, template class Section, class Interval, template class Alloc> class boost::icl::interval_map’ typedef boost::icl::interval_map, ShmemAllocator> MyMap;

error: expected a class template, got ‘ShmemAllocator {aka boost::interprocess::allocator, boost::interprocess::segment_manager, boost::interprocess::iset_index> >}’

error: invalid type in declaration before ‘;’ token

回答1:

For now, the compiler error indicates that you're passing a type as a template argument, where a (variadic) template template argument was expected.

You could technically achieve that using C++11 template aliasing¹:

template <typename T> using allocator = bip::allocator<T, smgr>;
template<typename T>  using set       = std::set<T, allocator<T> >;

template <typename Domain, typename Codomain>
using basic_map = icl::interval_map<Domain, Codomain,
        icl::partial_absorber, std::less, icl::inplace_plus, icl::inter_section, icl::discrete_interval<int, std::less>,
        allocator
    >;

However, it will not lead to working code as I've found in my livecoding session. The problem is that clearly Boost ICL doesn't currently support stateful allocators.

This means that not all constructors take an allocator instance to pass in the required state.

Sadly, this means that only a hacky custom allocator could conceivably work, where the custom allocator would refer to your shared memory segment by a global reference.

Demo

Here's a demo that shows proof-of-concept with such a global-segment allocator:

Live On Coliru

#include <boost/icl/interval_map.hpp>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <iostream>
#include <vector>
#include <set>

namespace bip = boost::interprocess;
namespace icl = boost::icl;

namespace shared {
    using segment = bip::managed_mapped_file;
    using smgr    = segment::segment_manager;
}

namespace {

    static bip::managed_mapped_file global_mm(bip::open_or_create, "./demo.bin", 1ul<<20);
    static bip::allocator<void, shared::smgr> global_alloc(global_mm.get_segment_manager());

    template <class T> struct SimpleAllocator : std::allocator<T> { // inheriting the nested typedefs only
        typedef T value_type;

        SimpleAllocator() : _alloc(global_alloc) {}
        template <class U> 
            SimpleAllocator(const SimpleAllocator<U> &other) : _alloc(other._alloc) {}

        T* allocate(std::size_t n)           { return std::addressof(*_alloc.allocate(n)); }
        void deallocate(T *p, std::size_t n) { _alloc.deallocate(p, n); }

        // optionals
        template <typename Other> struct rebind { typedef SimpleAllocator<Other> other; }; 
        bip::allocator<T, shared::smgr> _alloc;
    };

    template <class T, class U> bool operator==(const SimpleAllocator<T> &, const SimpleAllocator<U> &) { return true;  }
    template <class T, class U> bool operator!=(const SimpleAllocator<T> &, const SimpleAllocator<U> &) { return false; }
}

namespace shared {

    template <typename T> using allocator = SimpleAllocator<T>;
    template<typename T>  using set       = std::set<T, std::less<T>, allocator<T> >;

    template <typename Domain, typename Codomain>
    using basic_map = icl::interval_map<Domain, Codomain,
            icl::partial_absorber, std::less, icl::inplace_plus, icl::inter_section, icl::discrete_interval<int, std::less>,
            allocator
        >;

    using map      = basic_map<int, set<int> >;
    using interval = map::interval_type;
}

#include <iostream>

int main() {

    shared::map demo;
    for (auto&& element : {
            shared::map::value_type { shared::interval::right_open(4, 5), { 1, 7, } },
            shared::map::value_type { shared::interval::right_open(2, 6), { 1, 2, 3, } },
        })
    {
        demo.add(element);
        std::cout << "adding: " << element.first << ", result: " << demo << "\n";
    }
}

I used managed_mapped_file instead of maneged_shared_memory because the latter is not support on the online compiler.


¹ or using a "type function" in c++03