On 6/25/2013 6:23 PM, JOAQUIN M. LOPEZ MUĂ‘OZ wrote:
H Jin, ________________________________________ De: Boost [boost-bounces@lists.boost.org] en nombre de Jin Chen [chenjin92@gmail.com] Enviado el: lunes, 24 de junio de 2013 19:56 Para: Boost@lists.boost.org Asunto: [boost] Could the custom allocator for flyweight hashed_factory be the boost::interprocess::allocator?
I want to use boost::flyweight to do the dedupilcation of boost::interprocess::basic_string object. I wrote a small test program for that. In my program: The flyweight works fine with the interprocess::basic_string which uses the interprocess::allocator only with the default hashed_factory. The default hashed_factory is ideal except that it uses std::allocator instead of interprocess::allocator. I followed the http://www.boost.org/doc/libs/1_53_0/libs/flyweight/doc/tutorial/configurati... specifier of a custom hash_factory which can change its allocator to the interprocess one, but then got insanely long compiler error that is super hard to decode...
This is a very interesting exercise! There are several problems with your solution:
* First of all, hashed_factory, as Jonathan points out, assumes the allocator is default constructible. As boost::interprocess::allocator needs a segment manager at construction time, you can overcome this by writing a custom interprocess allocator that takes that info statically, i.e., from a type that returns the segment manager:
struct memory_dat_segment_manager { typedef ipc::managed_mapped_file::segment_manager type;
static type* get() { static ipc::managed_mapped_file* segment= new ipc::managed_mapped_file(ipc::open_or_create,"memory.dat",409600); return segment->get_segment_manager(); } };
template
struct segment_allocator: boost::interprocess::allocator { typedef boost::interprocess::allocator super; segment_allocator():super(SegmentManager::get()){} template<typename T2> segment_allocator(const segment_allocator
& x):super(x){} super& base(){return *this;}
template<typename T2> struct rebind{ typedef segment_allocator
other; }; friend void swap(segment_allocator& x,segment_allocator& y) { swap(x.base(),y.base()); } };
Note that segment_allocator is default constructible: the segment is created on the first call to memory_dat_segment_manager.
* Now, the holder class (www.boost.org/libs/flyweight/doc/tutorial/configuration.html#holders ) needs also to place its content in shared memory. As none of the holders provided by Boost.Flyweight does this, we must define our own:
template
struct segment_holder_class:boost::flyweights::holder_marker { static C& get() { static C* pc=SegmentManager::get()-> find_or_construct<C>(typeid(segment_holder_class).name())(); return *pc; } }; template<typename SegmentManager> struct segment_holder:boost::flyweights::holder_marker { template<typename C> struct apply { typedef segment_holder_class
type; }; }; Again, the holder uses a SegmentManager class to get access to the segment manager
* Finally, you need an interprocess locking policy (www.boost.org/libs/flyweight/doc/tutorial/configuration.html#locking ), and again this needs to be custom-defined (note that, in order for labeled_recursive_mutex to be default constructible, we get the info needed at construction time (the name of the mutex) from a Name class much in the same way we did with SegmentManager before for the allocator and holder):
template<typename Name> struct labeled_recursive_mutex { typedef boost::interprocess::named_recursive_mutex mutex_type;
operator mutex_type&() { static mutex_type mutex( boost::interprocess::open_or_create,Name::get()); return mutex; } };
template<typename Name> struct labeled_locking:boost::flyweights::locking_marker { typedef labeled_recursive_mutex<Name> mutex_type; typedef boost::interprocess::scoped_lock< typename labeled_recursive_mutex<Name>::mutex_type> lock_type; };
There's a technical issue here (if you don't get the following explanation you might as well skip it): As the mutex used by Boost.Flyweight is created by the holder class and we are using a shared-memory custom holder, it turns out labeled_recursive_mutex is thus created in shared memory. But boost::interprocess::named_recursive_mutex cannot be created in shared memory: we solve the problem with operator mutex_type&(), which actually creates the real mutex as a static variable in regular memory.
And we're done. Find attached a small complete program showing how to put all the pieces together.
I just wanted to say I found this response incredibly good.