"parent_from_member" proposal

Sometimes your design contains the typical model<->view / data/logic<->gui separation of classes/objects but at the same time it is static enough that it does not require the also typical dynamic way of connecting the two worlds through pointers and/or signals/events and/or function pointers/callbacks and/or ... In other words: - the two corresponding objects ('model/logic' and 'view/gui') always exist in the same memory space/process - the 'model' always exists when the 'view' exists - one does not mind compile time coupling/compile time hit that will occur if the 'model' and the 'view' classes know which methods they must call on 'the other' object as opposed to knowing which slots to expose/signals to connect to. In such a scenario the typical 'dynamic connection/coupling' approach unnecessarily incurrs overhead both in terms of verbosity and efficiency...it is 'easier' if the 'model' holds the 'view' object by value (or a wrapper class holds them both or whatever other solution as long as their offset in memory is constant)...then the the 'model' knows its 'view' instance implicitly from its this pointer (only a positive offset is required) but the 'view' instance does not know its 'model' instance implicitly (using 'conventional' methods) although it is infact also only a matter of offset calculation from its this pointer (only a negative one in this situation)... Now, if we take into account that the 'model' class knows the name of its 'view' member it can 'create' a pointer-to-member to it thus enabling us to create a sort of an extended and templated version of the standard C offset macro (from stddef.h). Using this 'template offset' functor (called ParentFromMember here, provided by the 'model' class) the 'view' class/instance can now also get to its 'model' instance implicitly from its this pointer (without knowing exactly how or where it exists in relation to the 'model' instance). template <class T> struct DummyStorage { typedef typename std::tr1::aligned_storage < sizeof( T ), std::tr1::alignment_of<T>::value >::type type; }; template < class Parent, class Member, Member Parent::* pMember
class ParentFromMember { public: typedef typename std::tr1::remove_const<Parent>::type Parent; typedef typename std::tr1::remove_const<Member>::type Member; public: Parent & operator()( Member & member ) const { DummyStorage<Parent>::type const parentStorage; Parent const * const pDummyParent( reinterpret_cast<Parent const *>( &parentStorage ) ); ptrdiff_t const offset ( reinterpret_cast<char const *>( &( pDummyParent->*pMember ) ) - reinterpret_cast<char const *>( pDummyParent ) ); return *reinterpret_cast<Parent *>( reinterpret_cast<char *>( &member ) - offset ); } Parent const & operator()( Member const & member ) const { return operator()( const_cast<Member &>( member ) ); } }; ...the DummyStorage part is just 'added-paranoia'...the whole operator() function could (probably ?) be simplified to work just like the offset macro in MSVC's stddef.h (in fact, with MSVC, both approaches produce the same code)...: Parent & operator()( Member & member ) const { ptrdiff_t const offset( reinterpret_cast<ptrdiff_t>( &( reinterpret_cast<Parent const *>( 0 )->*pMember ) ) ); return *reinterpret_cast<Parent *>( reinterpret_cast<char *>( &member ) - offset ); } .... so if you have class View { void someViewFunc(); }; class Model { private: View v; public: typdef ParentFromMember<Model, View, &Model::v> ViewModel; void someModelFunc(); } void View::someViewFunc() { Model & myModel( Model::ViewModel()( *this ) ); myModel.someModelFunc(); } ... If this has any chance of not being against the standard and/or "by-the-book" conventions it could probably be augmented with additional debugging/paranoia checks (for example that classes that would use this would be required to derive from some utility class and/or hold the "Member" object in some templated wrapper that would add GUID-like headers in front of objects in memory to assert that the conversions were called on valid objects and things like that)... The whole approach could also be extended with utility classes like FusionContainerFromMember, ParentFromOptionalMember, MemberFromMember and such... One 'big' problem is that this breaks MSVC (tested with 9.0 SP1 and 10b1) with a compiler error (at the typedef line) when the Parent class (in this case the Model class) is a template class (it compiles fine with the online Comeau compiler). A workaround (for MSVC) that might come to mind is to place the typedef outside the body of the template Parent class (it can also be in a another class) but this no solution as template typedefs do not work and an out-of-body template would require public access to the member/encapsulation breakage...so far the only acceptable solution I could find for MSVC is to use a wrapper metafunction that returns the required ParentFromMember<> specialization... -- "That men do not learn very much from the lessons of history is the most important of all the lessons of history." Aldous Huxley
participants (1)
-
Domagoj Saric