[Interprocess] shared_ptr in shared memory and inheritance

Hello,
we would like to store collections of differents objects derived from a same
base class in a shared
memory segment, and use polymorphism to manipulate them using
shared_ptr<base>.
But as we can't have virtual functions in shared memory we are exploring
this solution :
We designed 2 parallels hierarchies of classes
- A hierarchy of empty objects in process memory with virtual functions
(polymorphism) (ex : class A)
- A hierarchy of data objects in shared memory without virtual functions
(class A_data)
A "process memory" object stores a weak_ptr to the "shared memory" data
object.
Use of data are encapsuled in accessors which take care of weak_ptr locking
and concurrent access.
I don't know if this is a good design (any opinions ?) but it seems to work
well... until the destruction
of "shared memory" objects.
The wrong destructor is used (the base one) instead of the destructor of the
derived class,
probably because the destructor is not virtual...
So I'm currently stuck and I'm looking for some advices.
Here is a little code demonstrating the problem :
#include

You're probably doing something wrong, shared_ptr doesn't require
virtual destructor to call the correct destructor. Here is a working
example: http://codepad.org/sktELTDk
Emil Dotchevski
Reverge Studios, Inc.
http://www.revergestudios.com/reblog/index.php?n=ReCode
On Mon, Feb 9, 2009 at 9:26 AM, Gaetan Gaumer
Hello, we would like to store collections of differents objects derived from a same base class in a shared memory segment, and use polymorphism to manipulate them using shared_ptr<base>. But as we can't have virtual functions in shared memory we are exploring this solution : We designed 2 parallels hierarchies of classes - A hierarchy of empty objects in process memory with virtual functions (polymorphism) (ex : class A) - A hierarchy of data objects in shared memory without virtual functions (class A_data)
A "process memory" object stores a weak_ptr to the "shared memory" data object. Use of data are encapsuled in accessors which take care of weak_ptr locking and concurrent access.
I don't know if this is a good design (any opinions ?) but it seems to work well... until the destruction of "shared memory" objects. The wrong destructor is used (the base one) instead of the destructor of the derived class, probably because the destructor is not virtual... So I'm currently stuck and I'm looking for some advices.
Here is a little code demonstrating the problem : #include
#include #include <iostream> using namespace boost::interprocess; using namespace std; class base { protected : int id; public: base(int i=0):id(i){}; ~base(){cout << "base dtor" << endl;} int getId() const {return id;} }; class derived : public base { protected: double data; public: derived(int i=0, double d=0): base(i), data(d){}; ~derived(){cout << "derived dtor" << endl;} double getData(){return data;} }; typedef managed_shared_ptr ::type base_shared_ptr; int main (){ fixed_managed_shared_memory segment(open_or_create,"sharedPtrSharedMemoryTest", 2048); // does not compile but that's what I would like //base_shared_ptr baseSP = make_managed_shared_ptr(segment.construct<derived>(anonymous_instance)(2,3.0), segment); // turnaround base_shared_ptr baseSP = make_managed_shared_ptr((base*)segment.construct<derived>(anonymous_instance)(2,3.0), segment); cout << "baseSP->getId() =" << baseSP->getId(); cout <<", baseSP->getData() = " << static_pointer_cast<derived>(baseSP)->getData() << endl; baseSP.reset(); // don't know why but base dtor is called 3 times !!! } I can live with static_pointer_cast<> because they'll be hidden in accessors. But I really need that the right destructor (here ~derived()) will be called as it's cause some asserts in real case : include/boost/interprocess/detail/segment_manager_helper.hpp:176: static boost::interprocess::detail::block_header* boost::interprocess::detail::block_header::block_header_from_value(const void*, size_t, size_t): Assertion `hdr->m_value_alignment == algn' failed. The commented call to make_managed_shared_ptr makes gcc complains with (thanks gfilt for formatting) : sharedPtrSharedMemoryTest.cc:30: error: conversion from `boost::interprocess::shared_ptr< derived , boost::interprocess::allocator< void , boost::interprocess::segment_manager< char , boost::interprocess::rbtree_best_fit< boost::interprocess::mutex_family, void *, 0u >, boost::interprocess::iset_index > >, boost::interprocess::deleter< derived , boost::interprocess::segment_manager< char , boost::interprocess::rbtree_best_fit< boost::interprocess::mutex_family, void *, 0u >, boost::interprocess::iset_index > > >' to non-scalar type `boost::interprocess::shared_ptr< base , boost::interprocess::allocator< void , boost::interprocess::segment_manager< char , boost::interprocess::rbtree_best_fit< boost::interprocess::mutex_family, void *, 0u >, boost::interprocess::iset_index > >, boost::interprocess::deleter< base , boost::interprocess::segment_manager< char , boost::interprocess::rbtree_best_fit< boost::interprocess::mutex_family, void *, 0u >, boost::interprocess::iset_index > > >' requested Any help will be welcome. Gaetan _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

Emil Dotchevski wrote:
You're probably doing something wrong, shared_ptr doesn't require virtual destructor to call the correct destructor. Here is a working example: http://codepad.org/sktELTDk
Yes, it needs a virtual destructor if it's a shared_ptr to the base class, otherwise you can't achieve type erasure. This virtual call does not happen with boost::interprocess::shared_ptr because virtuality is forbidden in shared memory. Regards, Ion

2009/2/9 Ion Gaztañaga
Emil Dotchevski wrote:
You're probably doing something wrong, shared_ptr doesn't require virtual destructor to call the correct destructor. Here is a working example: http://codepad.org/sktELTDk
Yes, it needs a virtual destructor if it's a shared_ptr to the base class, otherwise you can't achieve type erasure. This virtual call does not happen with boost::interprocess::shared_ptr because virtuality is forbidden in shared memory.
Regards,
Ion
In boost::shared_ptr doc http://www.boost.org/doc/libs/1_38_0/libs/smart_ptr/shared_ptr.htm#Members It's said : "This constructor has been changed to a template in order to remember the actual pointer type passed. The destructor will call *delete* with the same pointer, complete with its original type, even when *T* does not have a virtual destructor, or is *void*." So I thought Emil is right and that's why I tried : base_shared_ptr baseSP = make_managed_shared_ptr(segment.construct<derived>(anonymous_instance)(2,3.0), segment); I even tried something like this : base_shared_ptr baseSP = base_shared_ptr(segment.construct<derived>(anonymous_instance)(1,3.0), segment.get_allocator<void>(), segment.get_deleter<derived>() ); But this does not compile. But as boost::interprocess::shared_ptr is based on boost::shared_ptr I thought it could work.... Is it really impossible to achieve the same behavior in boost::interprocess::shared_ptr than in boost::shared_ptr ? I looked at the code (of both shared_ptr.hpp) and I don't see where is the lock. But I'm surely not enough skilled to write the right ctor by myself. But It would solve my problem, isnt'it ? Gaetan PS : By the way, thanks Ion and others for this great work on boost in general and the Interprocess library in particular.

Gaetan Gaumer wrote:
In boost::shared_ptr doc http://www.boost.org/doc/libs/1_38_0/libs/smart_ptr/shared_ptr.htm#Members It's said : "This constructor has been changed to a template in order to remember the actual pointer type passed. The destructor will call *delete* with the same pointer, complete with its original type, even when *T* does not have a virtual destructor, or is *void*."
Sorry, I spoke too fast. I was trying to say that if you cast your type to the base class and they construct a shared_ptr<Base>, then you loose all your polymorphism. shared_ptr's template constructor does this for you: shared_ptr ptr only knows the real type of the object in that constructor and that compile-time information is going to disappear when the templated constructor ends, so shared_ptr constructs a polymorphic shared_count that will call the correct deleter when needed: template<class Y> explicit shared_count( Y * p ): pi_( 0 ) { //sp_counted_impl_p has VIRTUAL functions pi_ = new sp_counted_impl_p<Y>( p ); //... } sp_counted_impl_p has type Y so it calls Y's constructor and sp_counted_impl_p is polymorphic so it can be casted to sp_counted_base without loosing functionality. So you always need virtual functions to get type erasure with shared_ptr. And that's why you can't do this with interprocess's shared_ptr.
Is it really impossible to achieve the same behavior in boost::interprocess::shared_ptr than in boost::shared_ptr ?
I afraid you can't. shared memory forbids any type of run-time polymorphism and that includes shared_ptr.
PS : By the way, thanks Ion and others for this great work on boost in general and the Interprocess library in particular.
Thanks for using Boost in general and Interprocess in particular ;-) Regards, Ion

Gaetan Gaumer wrote:
Hello, we would like to store collections of differents objects derived from a same base class in a shared memory segment, and use polymorphism to manipulate them using shared_ptr<base>. But as we can't have virtual functions in shared memory we are exploring this solution : We designed 2 parallels hierarchies of classes - A hierarchy of empty objects in process memory with virtual functions (polymorphism) (ex : class A) - A hierarchy of data objects in shared memory without virtual functions (class A_data)
A "process memory" object stores a weak_ptr to the "shared memory" data object. Use of data are encapsuled in accessors which take care of weak_ptr locking and concurrent access.
I don't know if this is a good design (any opinions ?) but it seems to work well... until the destruction of "shared memory" objects. The wrong destructor is used (the base one) instead of the destructor of the derived class, probably because the destructor is not virtual... So I'm currently stuck and I'm looking for some advices.
First of all, if you use fixed_managed_shared_memory, you want to specify the same base address in ever process, otherwise, you're code won't work. You solve your issue, I'm afraid you will need to build a table of pointers to destructors indexed by a unique number per type (the same number in all processes), so that when the base class destructor is called, it makes a lookup in the table and calls the correct "destructor" function. Not easy, but I think it's the only way. Your hierarchy couldn't be expanded by users (they need to register their destructor). It's a limited type of "polymorphism". Regards, Ion

First of all, if you use fixed_managed_shared_memory, you want to specify the same base address in ever process, otherwise, you're code won't work.
Is it enough to create the segment with the same name in each process : eg a call to the line below in each process : fixed_managed_shared_memory segment(open_or_create,"sharedPtrSharedMemoryTest", 2048);
You solve your issue, I'm afraid you will need to build a table of pointers to destructors indexed by a unique number per type (the same number in all processes), so that when the base class destructor is called, it makes a lookup in the table and calls the correct "destructor" function. Not easy, but I think it's the only way. Your hierarchy couldn't be expanded by users (they need to register their destructor). It's a limited type of "polymorphism".
I understand your solution but I don't know wich method I have to redefine. Because the problem is not really the destructor, but rather the "deleter". And I don't find how to redefine (and register) the base deleter to call the derived one. Could you help me (again...) Regards, Gaëtan

Gaetan Gaumer wrote:
First of all, if you use fixed_managed_shared_memory, you want to specify the same base address in ever process, otherwise, you're code won't work.
Is it enough to create the segment with the same name in each process : eg a call to the line below in each process : fixed_managed_shared_memory segment(open_or_create,"sharedPtrSharedMemoryTest", 2048);
You don't have any guarantee that the OS will map it in the same address in both processes.
I understand your solution but I don't know wich method I have to redefine. Because the problem is not really the destructor, but rather the "deleter". And I don't find how to redefine (and register) the base deleter to call the derived one.
Could you help me (again...)
Sorry, it was just a fast idea that I haven't elaborated. But I guess this will be hard to implement. Inheritance and shared memory don't work very well
Regards, Gaëtan
Regards, Ion

2009/2/12 Ion Gaztañaga
Gaetan Gaumer wrote:
First of all, if you use fixed_managed_shared_memory, you want to specify the same base address in ever process, otherwise, you're code won't work.
Is it enough to create the segment with the same name in each process : eg a call to the line below in each process : fixed_managed_shared_memory segment(open_or_create,"sharedPtrSharedMemoryTest", 2048);
You don't have any guarantee that the OS will map it in the same address in both processes.
Ok, I'll force the mapping to the same address for all the processes.
I understand your solution but I don't know wich method I have to
redefine. Because the problem is not really the destructor, but rather the "deleter". And I don't find how to redefine (and register) the base deleter to call the derived one. Could you help me (again...)
Sorry, it was just a fast idea that I haven't elaborated. But I guess this will be hard to implement. Inheritance and shared memory don't work very well
I tried to implement that solution by writing a customDeleter inspired
by boost::interprocess::deleter
where the only difference is the operator() :
template
participants (3)
-
Emil Dotchevski
-
Gaetan Gaumer
-
Ion Gaztañaga