#include <hello> I was recently having a case where I was coupling two objects loosely. Both needed to know about each other, but the connection was not static, they sometimes where separated and reconnected somewhere else. For the curious, I was connecting a propertylist-widget with a container of entries for the list.
I currently fixed the issue by making some friend-declarations, some asserts and just 'paying attention that it works', but this is of course not satisfactory. I thus started thinking about a bidi_ptr<> template.... Any suggestions ? Is this a worthwhile idiom ? cheers Ulrich Eckhardt Outline: - takes two types as template-arguments, the types of the owners that are to be connected. - bidi_ptr<>s come in pairs, bidi_ptr<A,B> is complementary to bidi_ptr<B,A> - every bidi_ptr mainly consists of two pointers, one points to the owning object (m_owner), the other to the complementary bidi_ptr<> (m_peer) - assignment is defined as 'void bidi_ptr<A,B>::operator=(bidi_ptr<B,A>&rhs)' Note that is doesn't return a reference and that 'rhs' is not a const reference. This is used to 'connect' two peers. Also note that 'a = b' has the same effect as 'b = a'. - destruction of either bidi_ptr results in the peer being nulled - redirecting a pointer to a new peer nulls the former peer - pointer-like behaviour (*,->,get()) is implemented by accessing 'm_peer->m_owner' - operator! and conversion to a boolean are mostly copied from boost::shared_ptr TODO: - (how) can I implement that I can connect a bidi_ptr<A, Base> to a bidi_ptr<Derived,A> ? - bidi_ptr needs to be initialized with 'this' and store that pointer. I'm a bit dissatisfied with that, because it effectively doubles the memory-requirement and is not necessary (oversimplified example: 'owner = this-0x8') since it should easily be computed. - I have no idea if operator safe_bool() is properly implemented, because I don't understand the whys and hows of it, anyone a pointer to the rationale for it ? - using assignment for connecting peers warps the 'normal' modus operandi of assignment, should I rather us an auxillary function ? - usually, I would make bidi_ptr<B,A> a friend of bidi_ptr<A,B>, because it needs access to its peers internals in order to not work recursively. This doesn't work with my compiler if B is the same as A, the compiler complains that any class is implicitly its own friend. Therefore all data is currently public. - what should operator-> or * do when there is no peer attached ? - provide an implementation that uses a non-template base-class with two void-pointers, with derived classes only doing the casts. Measure executable size with and without.
#include "bidi_ptr.h" #include <iostream> struct X; struct Y; struct Z; struct X { X():m_pY(this), m_pX(this){} void test() const; void notify() const { std::cerr << "X::notify()\n"; } bidi_ptr<X,Y> m_pY; bidi_ptr<X,X> m_pX; }; struct Y { Y():m_pX(this){} void notify() const { std::cerr << "Y::notify()\n"; } bidi_ptr<Y,X> m_pX; }; void X::test() const { std::cerr << "X::test()\n"; if(m_pX) m_pX->notify(); if(m_pY) m_pY->notify(); } int main() { X x; { Y y; connect(x.m_pY, y.m_pX); connect(x.m_pX, x.m_pX); x.test(); } x.test(); return 0; }
#ifndef BIDI_SMART_POINTER_H #define BIDI_SMART_POINTER_H #include <cassert> template<typename ThisType, typename OtherType> class bidi_ptr { typedef bidi_ptr<ThisType,OtherType> this_type; typedef bidi_ptr<OtherType,ThisType> complement_type; // not default-constructible bidi_ptr(); public: /** you always have to init a bidi-pointer with the owning object, i.e. this. */ bidi_ptr(ThisType* owner):m_owner(owner),m_peer(0) { assert(m_owner);} /** connect two pointers Note: - 'p1 = p2' as the same postcondition as 'p2 = p1' - return-type is void because expressions like 'p1=p2=p3' make no sense - the other pointer is taken by non-const reference */ void operator=(bidi_ptr<OtherType,ThisType>& other) { if(m_peer!=&other) { other.reset(); reset(); m_peer = &other; other.m_peer = this; } } // friend class bidi_ptr<OtherType,ThisType>; ~bidi_ptr() { reset(); } void reset() { if(m_peer) m_peer->m_peer = 0; m_peer = 0; } typedef bool (this_type::*safe_bool)() const; operator safe_bool() const { return m_peer?&this_type::operator!:0; } bool operator!()const { return m_peer == 0; } OtherType* get() const { assert_valid(); return m_peer?m_peer->m_owner:0; } OtherType* operator->() const { return get(); } OtherType& operator*() const { return *get(); } // private: // - must have owner // - if connected, peer must be connected to this void assert_valid() const { assert(m_owner); if(m_peer) assert(this == m_peer->m_peer); } ThisType*const m_owner; bidi_ptr<OtherType,ThisType>* m_peer; }; template<typename ThisType, typename OtherType> void connect(bidi_ptr<OtherType,ThisType>& p1, bidi_ptr<ThisType,OtherType>& p2) { p1 = p2; } #endif
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost