Boost logo

Boost :

Subject: [boost] "parent_from_member" proposal
From: Domagoj Saric (domagoj.saric_at_[hidden])
Date: 2009-10-02 07:21:55


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 

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk