Inferno  0.2
itemise.hpp
Go to the documentation of this file.
00001 #ifndef ITEMISE_HPP
00002 #define ITEMISE_HPP
00003 
00004 #include <stdio.h>
00005 #include <vector>
00006 #include "common/common.hpp"
00007 #include <inttypes.h>
00008 
00009 using namespace std;
00010 
00011 // Note about multiple inheritance:
00012 // If Itemiser::Element is at the base of a diamond, itemiser will see it twice,
00013 // *even if* virtual inheritance is used. This may be a compiler bug in which case the above
00014 // is true only for GCC4.3. Anyway, if we see it twice it will have the same address,
00015 // so we de-duplicate during itemise algorithm.
00016 
00017 class Itemiser : public virtual Traceable
00018 {
00019 public:
00020     class Element
00021     {
00022     public:
00023         virtual ~Element() {}
00024         Element &operator=( const Element &other )
00025         {
00026             if( (uintptr_t)this >= (uintptr_t)dstart &&
00027                 (uintptr_t)this < (uintptr_t)dend )
00028             {
00029                 uintptr_t ofs = (uintptr_t)this - (uintptr_t)dstart;
00030                 FOREACH( uintptr_t x, v )
00031                     if( x==ofs )
00032                       return *this; // don't insert if in there already, see above
00033                 v.push_back( ofs );
00034                 //TRACE("%d ", ofs );
00035             }
00036             return *this;
00037         }
00038     };
00039     
00040   template< class ITEMISE_TYPE >
00041   inline static vector< uintptr_t > ItemiseImpl( const ITEMISE_TYPE *itemise_architype )
00042   {
00043     //TRACE("Static itemise %s ", typeid(*itemise_architype).name() );
00044     (void)itemise_architype;
00045     ITEMISE_TYPE d( *itemise_architype );
00046     ITEMISE_TYPE s( *itemise_architype );
00047     dstart = (char *)&d;
00048     dend = dstart + sizeof(d);
00049     v.clear();
00050 
00051     // This is the assignment that will be detected
00052     //TRACE("Assigning ");
00053     d = s;
00054     //TRACE(" done\n");
00055 
00056     return v;
00057   }
00058 
00059   template< class ITEMISE_TYPE >
00060   inline static const vector< uintptr_t > &BasicItemiseStatic( const ITEMISE_TYPE *itemise_architype )
00061   {
00062     // Just a cache on ItemiseImpl()
00063     static vector< uintptr_t > v;
00064     static bool done=false;
00065     if(!done)
00066     {
00067       v = ItemiseImpl( itemise_architype );
00068       done = true;
00069     }
00070     return v;
00071   }
00072 
00073   template< class ITEMISE_TYPE >
00074     inline static const vector< Itemiser::Element * > ItemiseStatic( const ITEMISE_TYPE *itemise_architype,
00075                                                                      const Itemiser *itemise_object )
00076     {
00077         ASSERT( itemise_architype )("Itemiser got itemise_architype=NULL\n");
00078         ASSERT( itemise_object )("Itemiser got itemise_object=NULL\n");
00079         
00080         // Do a safety check: itemise_object we're itemising must be same as or derived
00081     // from the architype, so that all the architype's members are also in itemise_object.        
00082         ASSERT( dynamic_cast<const ITEMISE_TYPE *>(itemise_object) )
00083               ( "Cannot itemise because itemise_object=")(*itemise_object)
00084               ( " is not a nonstrict subclass of itemise_architype=")(*itemise_architype);
00085     
00086     // Do the pointer math to get "elements of A in B" type behaviour
00087     // This must be done in bounce because we need the architype's type for the dynamic_cast
00088         const vector< uintptr_t > &vofs = BasicItemiseStatic( itemise_architype );
00089 
00090         const ITEMISE_TYPE *target_object = dynamic_cast<const ITEMISE_TYPE *>(itemise_object);
00091         vector< Itemiser::Element * > vout;
00092         FOREACH( uintptr_t ofs, vofs )
00093             vout.push_back( (Element *)((const char *)target_object + ofs) );
00094 
00095         return vout;
00096     }
00097 
00098   template< class ITEMISE_TYPE >
00099   inline static Itemiser::Element *ItemiseIndexStatic( const ITEMISE_TYPE *itemise_object,
00100                                                    int i )
00101   {
00102     const vector< uintptr_t > &v = BasicItemiseStatic( itemise_object );
00103     ASSERT( i>=0 );
00104     ASSERT( i<v.size() );
00105     uintptr_t ofs = v[i];
00106     return (Element *)((const char *)itemise_object + ofs);
00107   }
00108 
00109   template< class ITEMISE_TYPE >
00110   inline static int ItemiseSizeStatic( const ITEMISE_TYPE *itemise_object )
00111   {
00112     const vector< uintptr_t > &v = BasicItemiseStatic( itemise_object );
00113     return v.size();
00114   }
00115 
00116   static const char *dstart;
00117     static const char *dend;
00118     static vector<uintptr_t> v;
00119     
00120     virtual vector< Itemiser::Element * > Itemise(const Itemiser *itemise_object) const = 0;
00121 };
00122 
00123 #define ITEMISE_FUNCTION \
00124     virtual vector< Itemiser::Element * > Itemise( const Itemiser *itemise_object = 0 ) const  \
00125     { \
00126         return Itemiser::ItemiseStatic( this, itemise_object ? itemise_object : this ); \
00127     } \
00128     virtual Itemiser::Element *ItemiseIndex( int i ) const  \
00129     { \
00130         return Itemiser::ItemiseIndexStatic( this, i ); \
00131     } \
00132   virtual int ItemiseSize() const  \
00133   { \
00134     return Itemiser::ItemiseSizeStatic( this ); \
00135   }
00136 #endif