seeking comments on 'weak_key_map' experiments

Hello, I've been doing too much Python recently - thanks to boost.python - and my return in the c++ world in quite painful. To help me manage some of my objects lifetime dependencies, I wanted an access to a system similar to the Python WeakKeyDictionary class : Mapping class that references keys weakly. Entries in the dictionary will be
discarded when there is no longer a strong reference to the key. This can be used to associate additional data with an object owned by other parts of an application without adding attributes to those objects.
I drafted a small 'weak_key_map' class based on boost weak_ptr, shared_ptr
deleters, and a base class enabling classes to be used as keys in the
weak_key_map.
A small use case example is shown after, and source of weak_key_map & co are
attached.
I was wandering if someone had experience to share about a similar system ?
For a first draft, it already serves me well - but, I'm not very happy with
to "pollute" keys with the base class 'enable_master_ptr'...
Any idea to improve all this ?
Best regards,
Nicolas.
int Master_count = 0;
class Master : public mgd::enable_master_ptr<Master>
{
public:
Master() { Master_count++; }
~Master() { Master_count--; }
};
int Dependent_count = 0;
class Dependent : boost::noncopyable
{
public:
Dependent() { Dependent_count++; }
~Dependent() { Dependent_count--; }
};
BOOST_AUTO_TEST_CASE( test_weak_key_map_lifeTime )
{
typedef boost::shared_ptr<Dependent> value_t;
typedef mgd::weak_key_map

AMDG Nicolas Lelong wrote:
I've been doing too much Python recently - thanks to boost.python - and my return in the c++ world in quite painful. To help me manage some of my objects lifetime dependencies, I wanted an access to a system similar to the Python WeakKeyDictionary class :
Mapping class that references keys weakly. Entries in the dictionary will be discarded when there is no longer a strong reference to the key. This can be used to associate additional data with an object owned by other parts of an application without adding attributes to those objects.
I drafted a small 'weak_key_map' class based on boost weak_ptr, shared_ptr deleters, and a base class enabling classes to be used as keys in the weak_key_map.
A small use case example is shown after, and source of weak_key_map & co are attached.
I was wandering if someone had experience to share about a similar system ?
For a first draft, it already serves me well - but, I'm not very happy with to "pollute" keys with the base class 'enable_master_ptr'...
Any idea to improve all this ?
Is there some way that this can interface with
boost::shared_ptr/weak_ptr better?
weak_key_map

Is there some way that this can interface with boost::shared_ptr/weak_ptr better? weak_key_map
m; boost::shared_ptr<int> k(new int(1)); m.insert(std::make_pair(k,5)); assert(*m.find(k) == 5);
k.reset();
assert(m.empty());
I'd also like that, but in this approach, I can't figure how to hook into the key shared_ptr deleter to have access to a notification of the smart_ptr deletion. I think we can currently get the current deleter of a shared_ptr, but we can't change it to combine it with another deleter. I rather suspect that the data structure would need to be implemented from
scratch. One of the nastier problems is how to deal with the case of: iterator invalidation. The safest solution is to make the presence of an iterator to a particular element guarantee that the key continues to exist.
That's a good point ! To make a parallel with Python, the iteration seems to be an issue : quote from Python doc *Note: Caution: Because a WeakValueDictionary is built on top of a Python dictionary, it must not change size when iterating over it. This can be difficult to ensure for a WeakValueDictionary because actions performed by the program during iteration may cause items in the dictionary to vanish "by magic" (as a side effect of garbage collection). * Reimplementing the whole map structure brings the task to another level of complexity...

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Friday 12 December 2008 10:11 am, Nicolas Lelong wrote:
Is there some way that this can interface with boost::shared_ptr/weak_ptr better? weak_key_map
m; boost::shared_ptr<int> k(new int(1)); m.insert(std::make_pair(k,5)); assert(*m.find(k) == 5);
k.reset();
assert(m.empty());
I'd also like that, but in this approach, I can't figure how to hook into the key shared_ptr deleter to have access to a notification of the smart_ptr deletion. I think we can currently get the current deleter of a shared_ptr, but we can't change it to combine it with another deleter.
Does the weak_ptr need to be removed from the map "instantly" when it expires? Could you just clean up the expired weak_ptrs at some later point? For example, when an iterator is incremented it might remove all the weak_ptrs it encounters while it is trying to find the next live weak_ptr in the map. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFJQpbM5vihyNWuA4URAkukAKDH8yFJ2VBMsby254fnr0V2X8qrbwCg3cuJ 8PV44+yDWat+MG+zwGHiZ5E= =79yT -----END PGP SIGNATURE-----

Does the weak_ptr need to be removed from the map "instantly" when it expires? Could you just clean up the expired weak_ptrs at some later point? For example, when an iterator is incremented it might remove all the weak_ptrs it encounters while it is trying to find the next live weak_ptr in the map.
In my current usage scenario and implementation, I rely on values being removed 'instantly'. The thing is that I never iterate these maps, I only query values associated to keys ; so in this case, a cleanup could be triggered, but it would increase the 'query' time cost - I tend to see this weak_key_map as a lightweight construction if possible... I also find that the 'instantly' matches well the shared_ptr philosophy, but that's not very objective.

Nicolas Lelong:
Does the weak_ptr need to be removed from the map "instantly" when it expires? Could you just clean up the expired weak_ptrs at some later point? For example, when an iterator is incremented it might remove all the weak_ptrs it encounters while it is trying to find the next live weak_ptr in the map.
In my current usage scenario and implementation, I rely on values being removed 'instantly'.
The thing is that I never iterate these maps, I only query values associated to keys ; so in this case, a cleanup could be triggered, but it would increase the 'query' time cost - I tend to see this weak_key_map as a lightweight construction if possible...
I also find that the 'instantly' matches well the shared_ptr philosophy, but that's not very objective.
Accessing unrelated objects on destruction/deletion does not quite fit the shared_ptr/weak_ptr philosophy. This introduces data races and coupling and locking issues in the presence of multiple threads. Having p.reset() in thread A, who knows nothing about weak key maps, access a map object which may well be in use at the moment in thread B, forces you to make your container tolerate concurrent modification, preferably without using locks (to avoid locking hierarchy violations). -- Peter Dimov http://www.pdplayer.com

Accessing unrelated objects on destruction/deletion does not quite fit the shared_ptr/weak_ptr philosophy.
I take for granted that you're right about this ! I was more thinking about the 'instantness' and not about the 'implicit coupling / lifetime dependency' :)
This introduces data races and coupling and locking issues in the presence of multiple threads.
You have a very good point here. I'm far from being proficient when comes threading issues and lock free programming. In fact, my current usage pattern of this construct is quite similar to A. Alexandrescu's WRRM map (Write Rarely Read Many) in this articlehttp://erdani.org/publications/cuj-2004-10.pdf. So, it is perhaps possible to have a similar contruct, with a minimalist interface (say only query / update), and only returning copies of stored objects. This way, the client have, when queried, a full copy of the value - that won't be deleted under its feet. That could come in complement to the fact that, for querying you need to hold a shared_ptr on the key, which should prevent another thread to delete the key instance during the query.
participants (4)
-
Frank Mori Hess
-
Nicolas Lelong
-
Peter Dimov
-
Steven Watanabe