#ifndef FINDSHARE_H
#define FINDSHARE_H
//see findshare.txt in doc folder
 
 
#include <hash_map>
#include "..\_geom\aaa_first.h"
 
namespace fsh
{    template< typename K, typename V >
    class mh // Meta-Handle< "keyType", "valueType" >
    {    typedef K const * pcK;
        typedef K* pK;
        typedef V const * pcV;
        typedef V* pV;
        //The following three static functions are to be specialized by the user:
        static size_t key_hash(pcK);    //left undefined, needs specialization
        static bool equal_keys(pcK, pcK);        //left undefined, needs specialization
        static void free_handle(pK,pV);        //left undefined, needs specialization
        //Functors needed by stdext::hash_map. They simply call the private, static,
        //user-specialized functions (above) for hash code gen. and equality test.
        //September 2006 update:
        //The stdext::hash_map that comes with vc7 is quite different from
        //the hash map I was using originally. Instead of taking separate functors
        //for hash and comparison, it takes a single argument of class value_compare.
        //Here's their usage example:
        /*
        // hash_map_value_comp.cpp
        // compile with: /EHsc
        #include <hash_map>
        #include <iostream>
        int main( )
        {
            using namespace std;
            using namespace stdext;
            hash_map <int, int, hash_compare<int, less<int> > > hm1;
            hash_map <int, int, hash_compare<int, less<int> > >
            ::value_compare vc1 = hm1.value_comp( );
            pair< hash_map<int,int>::iterator, bool > pr1, pr2;
            pr1= hm1.insert ( hash_map <int, int> :: value_type ( 1, 10 ) );
            pr2= hm1.insert ( hash_map <int, int> :: value_type ( 2, 5 ) );
            if( vc1( *pr1.first, *pr2.first ) == true )   
            {
                cout << "The element ( 1,10 ) precedes the element ( 2,5 )."
                    << endl;
            }
            else   
            {
                cout << "The element ( 1,10 ) does not precede the element ( 2,5 )."
                    << endl;
            }
            if( vc1 ( *pr2.first, *pr1.first ) == true )
            {
                cout << "The element ( 2,5 ) precedes the element ( 1,10 )."
                    << endl;
            }
            else   
            {
                cout << "The element ( 2,5 ) does not precede the element ( 1,10 )."
                    << endl;
            }
        }
        */
#if 0
        //I don't quite understand, but I've used stdext::hash_map before, and it
        //didn't really need any functors if the mapped type has a size_t conversion
        //operator (which returns a hash value), so I'll just comment out for now.
        struct hf //hash functor
        {    size_t operator()(pcK pcK) const
            {    return mh<K,V>::key_hash(pcK); } //key_hash is usr-specialization
        };
        struct eq //equality comparison functor
        {    bool operator()(pcK pcK1, pcK pcK2)
            {    return mh<K,V>::equal_keys(pcK1,pcK2); } //equal_keys 2b usr-specialized
        };
#endif
        friend struct hf; friend struct eq;
        //std::hash_map, like std::map, stores objects of type std::pair< key, data >
        //Class C will be the type of the data part of this pair. It is a
        //wrapper of the V template type whiC simply adds a ref-count to it.
        class C //Counted-const-value wrapper
        {    friend class mh<K,V>;
            C() : rc_(0) {}
            C(pV pv) : pv_(pv), rc_(0) {}
            pcV inc_usr() const { ++rc_; return pv_; }
            size_t dec_usr() const { return --rc_; }
            void clr_usr() const { rc_=0; }
            //copy ctor, copy assignment and dtor, as generated by the compiler, are
            //just fine; and used by stl::pair; so don't disable! No ownership issues.
            pcV pv_;
            mutable size_t rc_;
        };
    public:
        class M : public stdext::hash_map<pK,C/*,hf,eq*/>
        {
        public:
            M(/*const size_t sz*/) : stdext::hash_map<pK,C/*,hf,eq*/>(/*sz*/) {}
        };
    private:
        typedef typename M::iterator I;
        class R : public std::pair<typename stdext::hash_map<pK,C/*,hf,eq*/>::iterator,bool>
        {
        public:
            R(std::pair<typename stdext::hash_map<pK,C/*,hf,eq*/>::iterator,bool> const & ibp)
                : std::pair<typename stdext::hash_map<pK,C/*,hf,eq*/>::iterator,bool>(ibp) {}
        };
        //Our two non-static (per mh) data members:
        I    it_;    //just used to decrement the map entry's ref count
        pcV    pv_;    //mh-cached copy of the pv_ in the hash table entry
        //Static section: --dealing with the std::hash_map, common repository...
        static M * m_;        //the hash_map, allocated singleton-like.
        static M * the_map(); //instantiates a hf map on demand
        static void clear(); //clears and deletes the hf map
        static size_t uc_; //global use count, for hash_map de-allocation
        static void inc_usr(){ ++uc_; }
        static bool dec_usr(){ return --uc_==0; }
        //acquisition: (the heart of findshare):
        I acq(pK k);
    public:
        //ctor's and dtor
        explicit mh<K,V>() : it_(), pv_(NULL) {}
        explicit mh<K,V>(pK k);
        mh<K,V>(mh<K,V> const & h);
        mh<K,V>& operator=(pK k); //intended for uninitd only
        mh<K,V>& operator=(mh<K,V> const & h); //intended for uninitd only
        virtual ~mh<K,V>() = 0; //inherit struct from handle to remove long template names
        V const & operator()() const { assert(pv_); return *pv_; } //f'n call op. rets ref to val
        bool got_val() const { return pv_ != 0; }
        void set_val(pcV); //only for first-time-key handles
};    };
 
// static vars:
template< typename K, typename V >
typename fsh::mh<K,V>::M * fsh::mh<K,V>::m_ = 0 ; 
template< typename K, typename V >
 size_t fsh::mh<K,V>::uc_ = 0;
 
// singleton static fn's:
template< typename K, typename V >
typename fsh::mh<K,V>::M *  fsh::mh<K,V>::the_map()
{    if( !m_ )
    {    m_ = new M(/*777*/);
        uc_ = 0;
    }
    return m_;
}
template < typename K, typename V >
 void fsh::mh<K,V>::clear()
{    if( m_ )
    {    m_->clear();
        delete m_;
        m_ = 0;
}    }
 
//acquisition (the heart of findshare): "find, or else create & insert"
template < typename K, typename V >
typename fsh::mh<K,V>::I fsh::mh<K,V>::acq(pK pk)
{    static M* pm = the_map();
    assert( the_map() );
    I iter(pm->find(pk)); //find.. if found we're done; else...
    if( iter == pm->end() ) //i.e.: if key not found in hash map
    {    //pV pv = make_handle(pk); //user specialization that generates id
        R res // insert new key/value pair in hash map
          ( pm->insert( I::value_type( pk, NULL ) ) );
        assert(res.second); //make sure that insertion succeded
        iter = res.first;  //successful insert :-)
        (*iter).second.clr_usr(); //start ref-count at zero
    }
    return iter;
}
template < typename K, typename V >
 void fsh::mh<K,V>::set_val(pcV pv) //only for first-time-key handles
{    assert(!pv_); if(!pv_) pv_ = pv;
    assert(!(*it_).second.pv_);
    if(!(*it_).second.pv_) (*it_).second.pv_ = pv;        
}
 
//ctor, assignment and dtor:
template < typename K, typename V >
 fsh::mh<K,V>::mh(pK pcK)
 : it_(acq(pcK)), pv_((*it_).second.inc_usr()){inc_usr();}
template < typename K, typename V >
 fsh::mh<K,V>::mh(mh<K,V> const & h)
 : it_(h.it_), pv_((*it_).second.inc_usr()){inc_usr();}
template < typename K, typename V >
 fsh::mh<K,V>& fsh::mh<K,V>::operator=(pK pcK) //intended for uninitd only
{    assert( !pv_ );
    it_ = acq(pcK);
    pv_ = (*it_).second.inc_usr();
    inc_usr();
    return *this;
}
template < typename K, typename V >
 fsh::mh<K,V>& fsh::mh<K,V>::operator=(fsh::mh<K,V> const & h) //intended for uninitd only
{    assert( this != &h );
    assert( !pv_ );
    assert( h.pv_ );
    it_ = h.it_;
    pv_ = (*it_).second.inc_usr();
    inc_usr();
    return *this;
}
template < typename K, typename V >
 fsh::mh<K,V>::~mh()
{    M* pm;
    assert( the_map() );
    if( (pm=the_map()) )
    {    assert( pv_ );
        if( !(*it_).second.dec_usr() )
        {    assert( (*it_).first );
            pV temp_pv = const_cast<pV>(pv_);
            pK temp_pk = (*it_).first;
            the_map()->erase(it_);
            free_handle(temp_pk,temp_pv); //usr-def'd specialization
            if( dec_usr() ) clear();
        }
        else if( dec_usr() ) assert( false ); //ERROR: use-counts out of synC!
}    }
 
#endif
 
code/root/hpp/findshare.hpp.txt · Last modified: 2006/10/09 22:11 by chuck_starchaser