#if defined(_Windows) || defined(_MSC_VER) || defined (__GNUC__)
 #define  STRICT
 #include <windows.h>
#endif

#include <string>

#define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING
#define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/hashed_index.hpp>

//------------------------------------------------------------------------------

#define  UINT32  unsigned long
#define  STRING  std::string

//------------------------------------------------------------------------------

// choose one
//#define VERSION01
//#define VERSION02
//#define VERSION03
//#define VERSION04

//------------------------------------------------------------------------------

template<typename MAP, typename ELM, typename SC1, typename IT1, typename SC2, typename IT2>
class tCanMap {
  protected:
    mutable  MAP     _mpMap;     // map
    mutable  SC1    *_scNme;     // sorting criteria name       ... have to init from derived class
    mutable  SC2    *_scDat;     // sorting criteria name       ... have to init from derived class
  public:
                     tCanMap   ();
             MAP    &getmap    ()             const;
             UINT32  setmapelm (ELM    val);
#if defined (VERSION01) || defined (VERSION02)
    const    ELM    *getmapelm (STRING stNme) const;
    const    ELM    *getmapelm (UINT32 uiDat) const;
#endif
};

template<typename MAP, typename ELM, typename SC1, typename IT1, typename SC2, typename IT2>
tCanMap<MAP,ELM,SC1,IT1,SC2,IT2>::tCanMap() {
  this->_scNme = NULL;
  this->_scDat = NULL;
}

template<typename MAP, typename ELM, typename SC1, typename IT1, typename SC2, typename IT2>
MAP &tCanMap<MAP,ELM,SC1,IT1,SC2,IT2>::getmap() const {
  return this->_mpMap;
}

template<typename MAP, typename ELM, typename SC1, typename IT1, typename SC2, typename IT2>
UINT32 tCanMap<MAP,ELM,SC1,IT1,SC2,IT2>::setmapelm(ELM val) {
  if (this!=NULL) { this->_mpMap.insert(val); return 0; }
  return 1;
}

#if defined (VERSION01) || defined (VERSION02)
template<typename MAP, typename ELM, typename SC1, typename IT1, typename SC2, typename IT2>
const ELM *tCanMap<MAP,ELM,SC1,IT1,SC2,IT2>::getmapelm(STRING stNme) const {
  try {
    IT1 itr = this->_mpMap.template get<0>().find(stNme);
    if ( itr != this->_mpMap.template get<0>().end() ) { return &(*itr); }
  }
  catch(...) {}
  return NULL;
}

template<typename MAP, typename ELM, typename SC1, typename IT1, typename SC2, typename IT2>
const ELM *tCanMap<MAP,ELM,SC1,IT1,SC2,IT2>::getmapelm(UINT32 uiDat) const {
  try {
    IT2 itr = this->_mpMap.template get<1>().find(uiDat);
    if ( itr != this->_mpMap.template get<1>().end() ) { return &(*itr); }
  }
  catch(...) {}
  return NULL;
}
#endif

//------------------------------------------------------------------------------

class cClass00 {
  private:
    STRING _nme;
    UINT32 _dat;
  public:
           cClass00()                             {}
           cClass00(STRING nme, UINT32 dat)       { this->_nme=nme; this->_dat=dat; }
    STRING getnme  ()                       const { return this->_nme;              }
    UINT32 getdat  ()                       const { return this->_dat;              }
};

typedef boost::multi_index_container <
  cClass00,
  boost::multi_index::indexed_by <
    boost::multi_index::hashed_unique  < BOOST_MULTI_INDEX_CONST_MEM_FUN(cClass00,STRING,getnme) >,
    boost::multi_index::ordered_unique < BOOST_MULTI_INDEX_CONST_MEM_FUN(cClass00,UINT32,getdat) >
  >
> mClass00;

typedef mClass00::nth_index<0>::type mClass00ByNme;
typedef mClass00ByNme::iterator      mClass00ByNmeItr;

typedef mClass00::nth_index<1>::type mClass00ByDat;
typedef mClass00ByDat::iterator      mClass00ByDatItr;

//------------------------------------------------------------------------------

class cClass01 : public tCanMap<mClass00,cClass00,mClass00ByNme,mClass00ByNmeItr,mClass00ByDat,mClass00ByDatItr> {
  private:
    STRING _nme;
    UINT32 _dat;
    typedef tCanMap<mClass00,cClass00,mClass00ByNme,mClass00ByNmeItr,mClass00ByDat,mClass00ByDatItr> super;
  public:
           cClass01()                             {}
           cClass01(STRING nme, UINT32 dat)       { this->_nme=nme; this->_dat=dat; this->_scNme = &(this->_mpMap.get<0>()); this->_scDat = &(this->_mpMap.get<1>()); }
           cClass01(const cClass01& x): super(x), _nme(x._nme), _dat(x._dat) { this->_scNme = &(this->_mpMap.get<0>()); this->_scDat = &(this->_mpMap.get<1>()); }
           cClass01& operator=(const cClass01& x) { super::operator=(x); this->_nme=x._nme; this->_dat=x._dat; this->_scNme = &(this->_mpMap.get<0>()); this->_scDat = &(this->_mpMap.get<1>()); return *this;}
    STRING getnme()                         const { return this->_nme;                                                       }
    UINT32 getdat()                         const { return this->_dat;                                                       }
#if defined (VERSION03) || defined (VERSION04)
    const cClass00 *getmapelm (STRING stNme) const {
      try { if ( this!=NULL ) { mClass00ByNmeItr itr = this->_mpMap.get<0>().find(stNme); if ( itr != this->_mpMap.get<0>().end() ) { return &(*itr); } } }
      catch(...) {}
      return NULL;
    }
    const cClass00 *getmapelm (UINT32 uiDat) const {
      try { if ( this!=NULL ) { mClass00ByDatItr itr = this->_mpMap.get<1>().find(uiDat); if ( itr != this->_mpMap.get<1>().end() ) { return &(*itr); } } }
      catch(...) {}
      return NULL;
    }
#endif

};

typedef boost::multi_index_container <
  cClass01,
  boost::multi_index::indexed_by <
    boost::multi_index::hashed_unique  < BOOST_MULTI_INDEX_CONST_MEM_FUN(cClass01,STRING,getnme) >,
    boost::multi_index::ordered_unique < BOOST_MULTI_INDEX_CONST_MEM_FUN(cClass01,UINT32,getdat) >
  >
> mClass01;

typedef mClass01::nth_index<0>::type mClass01ByNme;
typedef mClass01ByNme::iterator      mClass01ByNmeItr;

typedef mClass01::nth_index<1>::type mClass01ByDat;
typedef mClass01ByDat::iterator      mClass01ByDatItr;

//------------------------------------------------------------------------------

class cClass02 : public tCanMap<mClass01,cClass01,mClass01ByNme,mClass01ByNmeItr,mClass01ByDat,mClass01ByDatItr> {
    typedef tCanMap<mClass01,cClass01,mClass01ByNme,mClass01ByNmeItr,mClass01ByDat,mClass01ByDatItr> super;
  public:
    cClass02() { this->_scNme = &(this->_mpMap.get<0>()); this->_scDat = &(this->_mpMap.get<1>()); }
    cClass02(const cClass02& x): super(x) { this->_scNme = &(this->_mpMap.get<0>()); this->_scDat = &(this->_mpMap.get<1>()); }
    cClass02& operator=(const cClass02& x) { super::operator=(x); this->_scNme = &(this->_mpMap.get<0>()); this->_scDat = &(this->_mpMap.get<1>()); return *this;}
#if defined (VERSION03) || defined (VERSION04)
    const cClass01 *getmapelm (STRING stNme) const {
      try { if ( this!=NULL ) { mClass01ByNmeItr itr = this->_mpMap.get<0>().find(stNme); if ( itr != this->_mpMap.get<0>().end() ) { return &(*itr); } } }
      catch(...) {}
      return NULL;
    }
    const cClass01 *getmapelm (UINT32 uiDat) const {
      try { if ( this!=NULL ) { mClass01ByDatItr itr = this->_mpMap.get<1>().find(uiDat); if ( itr != this->_mpMap.get<1>().end() ) { return &(*itr); } } }
      catch(...) {}
      return NULL;
    }
#endif
};

//------------------------------------------------------------------------------

int main(void) {
#if defined (VERSION01) || defined (VERSION03)
// works fine in any case
  cClass00  objClass00_01 = cClass00( "elm00_01", 1 );
  cClass00  objClass00_02 = cClass00( "elm00_02", 2 );
  cClass00  objClass00_03 = cClass00( "elm00_03", 3 );
  cClass00  objClass00_04 = cClass00( "elm00_04", 4 );

  cClass01  objClass01_01 = cClass01( "elm01_01", 11 );
  cClass01  objClass01_02 = cClass01( "elm01_02", 12 );

  objClass01_01.setmapelm( objClass00_01 );
  objClass01_01.setmapelm( objClass00_02 );
  objClass01_02.setmapelm( objClass00_03 );
  objClass01_02.setmapelm( objClass00_04 );

  cClass02  *ptrClass02_01  =  new cClass02();

  ptrClass02_01->setmapelm( objClass01_01 );
  ptrClass02_01->setmapelm( objClass01_02 );

  printf("START\n");
  printf("\n");
  printf( "1. try: %02d\n", ptrClass02_01->getmapelm("elm01_01")->getdat() );
  printf( "2. try: %02d\n", ptrClass02_01->getmapelm("elm01_02")->getdat() );
  printf("\n");
  printf( "3. try: %02d\n", ptrClass02_01->getmapelm("elm01_01")->getmapelm("elm00_01")->getdat() );
  printf( "4. try: %02d\n", ptrClass02_01->getmapelm("elm01_02")->getmapelm("elm00_04")->getdat() );
  printf("\n");
  printf( "5. try: %s\n", ptrClass02_01->getmapelm("elm01_01")->getmapelm(2)->getnme().c_str() );
  printf( "6. try: %s\n", ptrClass02_01->getmapelm("elm01_02")->getmapelm(3)->getnme().c_str() );
  printf("\n");
  printf("DONE\n");
#elif defined (VERSION02) || defined (VERSION04)
// run into segvault if object become overwritten OR
// object for filling multi_index is only a local variable of a filling function
  cClass02 *ptrClass02 = new cClass02();
  cClass01  objClass01;
  cClass00  objClass00;

  objClass01 = cClass01("elm01_01", 11);
  objClass00 = cClass00("elm00_01",  1); objClass01.setmapelm(objClass00);
  objClass00 = cClass00("elm00_02",  2); objClass01.setmapelm(objClass00);

  ptrClass02->setmapelm(objClass01); // insert objClass01

  objClass01 = cClass01("elm01_02", 12);
  objClass00 = cClass00("elm00_03",  4); objClass01.setmapelm(objClass00);
  objClass00 = cClass00("elm00_04",  3); objClass01.setmapelm(objClass00);

  ptrClass02->setmapelm(objClass01); // inserted object different than above

  printf("START\n");
  printf( "dat = %d\n", ptrClass02->getmapelm("elm01_01")->getdat() );  // seg_vault
//  printf( "dat = %d\n", ptrClass02->getmapelm("elm01_02")->getdat() );  // works fine
  printf("DONE\n");
#endif
}

