c++templatesheap-memoryshared-memoryboost-interprocess

boost::interprocess scoped_allocator AND Containers of containers NOT in shared memory


I have a similar question as before in boost::interprocess Containers of containers NOT in shared memory and How to I create a boost interprocess vector of interprocess containers but this time I like to use my class, which uses a scoped_allocator, also on the heap and shared memory.

Now I like to have both, is this possible? Attached a example with a working scoped_allocator but I have no idea how to template in this case the allocator of the class?

Thanks in advance :-)

Markus

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/algorithm/string.hpp>

namespace bi = boost::interprocess;
namespace bc = boost::container;

typedef bi::managed_shared_memory::segment_manager                         segment_manager_t;
typedef bc::scoped_allocator_adaptor<bi::allocator<void, segment_manager_t> >  void_allocator;

class CheckList {
typedef void_allocator::rebind<double>::other       double_allocator;
typedef bc::vector<double, double_allocator>         double_vector;
public:
    double_vector values;

    typedef void_allocator allocator_type;

    //Since void_allocator is convertible to any other allocator<T>, we can simplify
    //the initialization taking just one allocator for all inner containers.
    CheckList ( const allocator_type &void_alloc )
        :  values ( void_alloc ) {}

    CheckList ( CheckList const& other, const allocator_type &void_alloc )
        :  values ( other.values, void_alloc ) {}

    friend std::ostream &operator << ( std::ostream &os, const CheckList &o ) {
        for ( size_t i = 0; i < o.values.size(); i++ )  os << (i>0?", ":"") << o.values[i];
        return os;
    }
};

class Lists {
typedef void_allocator::rebind<CheckList>::other      checklist_allocator;
typedef bc::vector<CheckList, checklist_allocator>    checklist_vector;

public:
    Lists ( const void_allocator &void_alloc )
        : checklists ( void_alloc )
    {}
    checklist_vector checklists;

    friend std::ostream &operator << ( std::ostream &os, const Lists &o ) {
        for ( size_t i = 0; i < o.checklists.size(); i++ ) os << o.checklists[i]  << std::endl;
        return os;
    }
};


int main ( int argc, char **argv ) {


    if ( argc > 1 && (boost::iequals ( "clear", argv[1] ) || boost::iequals ( "c", argv[1] ) ) ) {
        std::cout << "Remove shared memory" << std::endl;
        bi::shared_memory_object::remove ( "MySharedMemory" );
    }


    //Create shared memory
    bi::managed_shared_memory segment ( bi::open_or_create,"MySharedMemory", 16*1024*1024 );

    //An allocator convertible to any allocator<T, segment_manager_t> type
    void_allocator alloc_inst ( segment.get_segment_manager() );

    Lists *lists = segment.find_or_construct<Lists> ( "Lists" ) ( alloc_inst );
    if ( lists->checklists.size() != 10 ) {
        std::cout << "Create Data" << std::endl;
        lists->checklists.resize ( 10 );
        for ( size_t i = 0; i < lists->checklists.size(); i++ ) {
            lists->checklists[i].values.resize ( i+1 );
            for ( size_t j = 0; j < lists->checklists[i].values.size(); j++ ) {
                lists->checklists[i].values[j] = j;
            }
        }
    } else {
        std::cout << "Data Exists" << std::endl;
    }
    std::cout << *lists << std::endl;
}

Solution

  • Well. I'm not sure what was the challenge, beyond... yes let's make the allocator a template argument, and define

    namespace Shared {
        typedef bi::managed_shared_memory::segment_manager                             segment_manager_t;
        typedef bc::scoped_allocator_adaptor<bi::allocator<void, segment_manager_t> >  void_allocator;
        using Lists = common::Lists<void_allocator>;
    }
    
    namespace Heap {
        typedef std::allocator<void> void_allocator;
        using Lists = common::Lists<void_allocator>;
    }
    

    So, I went ahead and actually made converting constructors:

    Live On Coliru

    namespace common {
        template <typename Alloc>
        class CheckList {
            typedef typename Alloc::template rebind<double>::other double_allocator;
            typedef bc::vector<double, double_allocator> double_vector;
    
        public:
            double_vector values;
    
            typedef Alloc allocator_type;
            CheckList(const allocator_type& void_alloc = allocator_type()) : values(void_alloc) {}
    
            template <typename Alloc2>
            CheckList(CheckList<Alloc2> const& other, const allocator_type& void_alloc = allocator_type())
            : values(void_alloc)
            {
                for(auto& v : other.values) values.emplace_back(v);
            }
    
            friend std::ostream& operator<<(std::ostream& os, const CheckList& o) {
                for (size_t i = 0; i < o.values.size(); i++)
                    os << (i?", ":"") << o.values[i];
                return os;
            }
        };
    
        template <typename Alloc>
        class Lists {
            typedef typename Alloc::template rebind<CheckList<Alloc> >::other checklist_allocator;
    
        public:
            typedef Alloc allocator_type;
            typedef bc::vector<CheckList<Alloc>, checklist_allocator> checklist_vector;
    
            template <typename Alloc2>
            Lists& operator=(Lists<Alloc2> const& other) {
                for(auto& cl : other.checklists) checklists.emplace_back(cl);
                return *this;
            }
    
            Lists(const Alloc& void_alloc = allocator_type()) : checklists(void_alloc) {}
            checklist_vector checklists;
    
            friend std::ostream& operator<<(std::ostream& os, const Lists& o) {
                for (size_t i = 0; i < o.checklists.size(); i++)
                    os << o.checklists[i] << '\n';
                return os;
            }
        };
    }
    

    This means you can now have a function that returns heap-based collections and assign it to a shared-memory version of the same:

    Heap::Lists generate_local() {
        Heap::Lists lists;
        Heap::Lists::checklist_vector::value_type v;
    
        for (int i=0; i<10; ++i) {
            v.values.emplace_back(i+1);
            lists.checklists.push_back(v);
        }
    
        return lists;
    }
    
    // later:
    
        Lists& lists = *segment.find_or_construct<Lists>("Lists")(alloc_inst);
    
        if (lists.checklists.size() != 10) {
            std::cout << "Create Data" << std::endl;
            auto x = generate_local();
            lists = std::move(x);
        }