3

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.

  • The solution to my first question was to use a template class with an allocator type
  • In my second previous question it turned out that using a scoped_allocator together with a container of containers within the shared memory makes live easier.

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;
}

1 Answer 1

3

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);
    } 
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.