Hello world!

Welcome to WordPress.com. This is your first post. Edit or delete it and start blogging!

Posted in Uncategorized | 1 Comment

http://www.devmaster.net/forums/showthread.php?t=3070

#include <map>
#include <vector>
#include <iostream>
#include <string>

template< class T > class GenericManager;

template< class T >
class GenericHandle
{
    friend class GenericManager<T>;
    int m_index;

    GenericHandle( int i )
        : m_index(i)
    {}

public:

    GenericHandle()
        : m_index(-1)
    {}

    bool IsValid() const
    {
        return m_index != -1;
    }
};

template< class T >
class GenericManager
{
public:

    typedef GenericHandle<T> Handle_t;

    ~GenericManager()
    {
        DestroyAll();
    }

    Handle_t Create( const T& prototype )
    {
        int idx = 0;
        T* p = new T(prototype);

        if( m_freeStore.size() )
        {
            idx = m_freeStore.back();
            m_freeStore.pop_back();
            m_objectStore[idx] = p;
        }
        else
        {
            idx = m_objectStore.size();
            m_objectStore.push_back( p );
        }
        
        // m_mapStore[name] = idx;
        // name would be an std::string parameter passed to this function as well

        return Handle_t(idx);
    }


    void Destroy( Handle_t& h )
    {
        T* p = m_objectStore[h.m_index];

        if( p )
        {
            delete p;
            m_objectStore[h.m_index] = 0;
            m_freeStore.push_back( h.m_index );
        }

        // Here's where things get slow if search capabilities are added
        /*
        iterator i = mapStore.begin()
        for each i in map
            if( i->second == h.m_index )
                erase iterator i and break.
        */

        h.m_index = -1;
    }    

    void DestroyAll()
    {
        m_freeStore.clear();

        for( std::size_t i = 0; i < m_objectStore.size(); ++i )
        {
            if( m_objectStore[i] )
            {
                delete m_objectStore[i];
            }
        }

        m_objectStore.clear();
    }

    T* GetObject( Handle_t h ) const
    {
        if( !h.IsValid() )
            return 0;

        return m_objectStore[h.m_index];
    }

/*  Can have this function with search capabilities
    Handle_t GetHandle( string name )
    {
        iterator i = map.find(name);
        if( i != end ) return Handle_t(i->second);
        return Handle_t(-1);
    }
*/

private:
    std::vector<int> m_freeStore;
    std::vector<T*> m_objectStore;

    // std::map<std::string, int> m_mapStore; // for searching
};


class SomeObject
{
    std::string m_name;

public:

    SomeObject( const std::string& name )
        : m_name(name)
    {}

    SomeObject( const SomeObject& o )
    {
        m_name = o.m_name;
    }

    void Work()
    {
        std::cout << "I am " << m_name << std::endl;
    }
};

int main()
{
    typedef GenericManager<SomeObject> ObjMgr_t;

    ObjMgr_t mgr;

    ObjMgr_t::Handle_t h1 = mgr.Create( SomeObject("one") );
    ObjMgr_t::Handle_t h2 = mgr.Create( SomeObject("two") );
    ObjMgr_t::Handle_t h3 = mgr.Create( SomeObject("three") );
    
    mgr.Destroy( h3 );

    ObjMgr_t::Handle_t h4 = mgr.Create( SomeObject("four") );

    if( mgr.GetObject( h1 ) )
        mgr.GetObject( h1 )->Work();
    if( mgr.GetObject( h2 ) )
        mgr.GetObject( h2 )->Work();
    if( mgr.GetObject( h3 ) )
        mgr.GetObject( h3 )->Work();
    if( mgr.GetObject( h4 ) )
        mgr.GetObject( h4 )->Work();

/*
    Seaching would allow for something like:
    Handle = Manager.getHandle( "my object" );
*/

    return 0;
}
Posted in Uncategorized | Leave a comment

http://scottbilas.com/publications/gem-resmgr/

Method

The job of a resource manager is to create resources on demand, hand them out to anyone who asks, and then eventually delete them. Handing out those resources as simple pointers is certainly easy and convenient, but it’s not a very safe way to do it. Pointers can “dangle” – one part of the system may tell the resource manager to delete a resource, which then immediately invalidates all other outstanding pointers. There’s no good way to prevent the dangling pointer problem from happening, and the only way we would find out that someone was attempting to dereference a deleted object is when the access violation dialog box comes up and the game crashes. The problem is that, with pointers, there’s no way to know how many references are outstanding, given that clients can copy the pointers as many times as they like without telling the manager about it.

Another problem is that the underlying data organization can’t change with pointers. Any reallocation of buffers would immediately invalidate all outstanding pointers. This becomes especially important with saving the game to disk. Pointers can’t be saved to disk because the next time the game is loaded, system memory will probably be configured differently or we may even be on a completely different machine. The pointers must be converted into a form that can be restored, which will probably be an offset or a unique identifier of some sort. Working around this problem isn’t exactly trivial and can require a lot of work to support in client code.

So it’s plainly not a good idea for a safe and flexible resource manager to be handing out pointers. Rather than using pointers, or attempting to write some kind of super-intelligent over-complicated “smart pointer”, we can add one layer of abstraction and use handles instead, and put the burden on the manager class. Handles are an ancient programming concept that API’s have been using with great success for decades. An example of this is the HANDLE type returned by the CreateFile() call in Win32’s file system. A file handle, representing an open file system object, is created through the CreateFile() call, passed to other functions such as ReadFile() and SetFilePointer() for manipulation, and then finally closed off with CloseHandle(). Attempting to call those functions with an invalid or closed handle will not cause a crash, but will instead return an error code. This method is efficient, safe, and easy to understand.

Handles almost always fit into a single CPU register for efficient storage in collections and passing as parameters to functions. They can be easily checked for validity and provide a level of indirection that allows the underlying data organization to change without invalidating any outstanding handles. This has significant advantages over passing around pointers. Handles can also be easily saved to disk, because the data structures they refer to can be reconstructed in the same order on a game restore. This allows the handles to be stored directly with no conversions necessary, because they are already natively in unique identifier form.

The Handle Class

A fast and safe way to represent handles is to use an unsigned integer composed of two bitfield components (this class appears in Listing 1). The first component (m_Index) is a unique identifier for fast dereferencing into the handle manager’s database. The handle manager can use this number however it likes, but perhaps the most efficient is as a simple index into an std::vector. The second component (m_Magic) is a “magic number” that can be used to validate the handle. Upon dereferencing, the handle manager can check to make sure that the magic number component of the handle matches up with its corresponding entry in the database.

The Handle class is very simple and really doesn’t do much except manage the magic number. Upon calling Init(), the handle will be given the next magic number, which auto increments and will wrap around if necessary. Note that the magic number is not intended to be a GUID. Its purpose is to serve as a very simple and fast validity check and is relying on the high improbability of a condition arising where one object happens to have the same index and magic number (via wrapping) as another. The magic number of zero is reserved for the “null handle” – where the handle’s data is zero. The default Handle constructor sets itself to null, a state that will return true on an IsNull() query. This is convenient to use for an error condition – a function that creates an object and returns a handle to it can just return a null handle to indicate an error occurred.

In most ways the Handle class will act as a read-only unsigned integer. It’s not intended to be modified after being created, though it can safely be assigned back to null to reset it. You’ll notice that it’s a parameterized class, taking a TAG type in order to fully define it. The template parameter TAG doesn’t do anything except differentiate among types of handles – an object of type TAG is never used anywhere in the system. The motivation here is type safety. Without parameterizing Handle, a handle meant for one type of resource could be passed to a function expecting a handle to a different type of resource without a complaint from the compiler. So to keep things safe, we create a new handle type, taking any unique symbol and using it for the parameter. The TAG type can really be anything so long as it’s unique across Handle types, but it’s convenient to define an empty struct and use that in the typedef for a handle, like this texture handle example:

Now we need a handle manager that is responsible for acquiring, dereferencing, and releasing objects (via handles) for a higher-level owner.

The HandleMgr Class

The HandleMgr class is a parameterized type composed of three main elements: a data store, a magic number store, and a free list (this class appears in Listing 2). The data store is simply a vector (or any other randomly accessible collection) of objects of type DATA. The DATA type, the first type parameter for HandleMgr, should be a very simple class that contains context information about the resource that it controls. For example, in a HandleMgr that manages files, the DATA type would probably just have the file handle and the name of the file:

struct FileEntry
{
    std::string m_FileName;
    HANDLE      m_FileHandle;  // OS file handle
};
 
struct tagFile  {  };
typedef Handle <tagFile> HFile;
typedef HandleMgr <FileEntry, HFile> FileHandleMgr;

This simple handle manager will maintain a set of context objects that correspond to all the open files that it knows about. The FileHandleMgr class will probably not be used directly by clients, but will instead be owned by another class (call it FileMgr) that handles the abstraction and knows about the problem domain (that is, what DATA is supposed to represent). This class might look something like this:

class FileMgr
{
    FileHandleMgr m_Mgr;
 
public:
    HFile OpenFile ( const char* name );
    bool  ReadFile ( HFile file, void* out, size_t bytes );
    bool  CloseFile( HFile file );
 
    // ...
};

Upon calling any of these methods, FileMgr will ask its m_Mgr to dereference the handle to get at the actual FileEntry object. After verifying that the dereference succeeded (it will fail on an invalid handle), it will then perform the operation.

For our HandleMgr class, each handle will reference exactly one element within the object store, plus its corresponding element in the magic number store. Dereferencing the handles to get at the actual FileEntry object is as simple as using the m_Index component of the handle as an index into the object store (a very fast operation).

When dereferencing the handle, the code will also check the m_Magic component against the same index in the magic number store to make sure the handle is valid. As handles are freed and reacquired, corresponding entries in the magic number store are updated with the new handle magic numbers. This nearly guarantees that “dangling” handles on released objects won’t refer to unexpected objects when the slots are filled by a later handle acquisition, but will instead simply fail to work and return an error code. Obviously, the magic number store will always have the same number of elements as the object store.

As objects are released, the handle manager will add the indexes of the slots they occupy to the free list. This saves it the trouble of needing to search through the object store to find an open slot, which results in a tasty O(n) complexity for new handle acquisition. It’s important to note that the DATA type is not your typical C++ class. It shouldn’t have constructors and destructors that do anything important, such as acquire and release local resources. Objects contained within the object store are constructed, destroyed, and copied as the vector class sees fit. Note that the std::string used in the sample FileEntry is “simple” enough for our needs – it’s reference-counted, which minimizes the impact of its constructors and destructors and makes it nearly free for vector to copy.

When asked to acquire an object from the store, we’ll likely end up reusing an object that has already been constructed but is no longer in use, as indicated by its entry in the free list. It will need its members reinitialized before it can be used, because it won’t have had the constructor call to set it up. When an object is freed from the store, it will not get destroyed, but will instead have its index added to the free list, and as such will need its resources manually freed. These minor limitations arise from the fact that we’re embedding our DATA type directly in the vector, rather than using pointers and creating and destroying the objects with new and delete for each handle acquisition and release. The major advantage here is speed, in that the objects don’t have to be completely brought up and shut down each time. To make things more convenient, the initialize/shutdown code can be moved into member functions for easy callback by the HandleMgr owner.

The amount of handle validation necessary may depend on the application, and could even be chosen through an additional template parameter for HandleMgr. For example, the test for an invalid handle may be found to be unnecessary and could be removed (though the debug assertion should always remain). For a more robust system where error handling is important, the code could, upon detecting an invalid handle, set an error condition, and then abort the function call.

Sample Usage

In Listing 3 I provide a sample texture manager class. This class allows clients to ask it for textures by name and will construct them on demand. It automatically unloads the textures on deletion, and provides a set of query functions to use the textures. The textures are indexed by name for speedy lookup to make sure that the same texture is not added to the store twice. It would be a simple exercise to add reference counting to this example to make it safer, replacing DeleteTexture() with ReleaseTexture().

For another (larger) sample of file handle usage, see the sample code for my GDC 2000 talk, It’s Still Loading? Designing an Efficient File System.

Notes

The HandleMgr class is very simple and is meant to illustrate some basic concepts, but it can be expanded in a number of ways, either with the existing HandleMgr or separate classes:

  • Create a HandleMgr that will work better with larger DATA objects, holding them indirectly through pointers. It would also allow hiding of the data structure to clients.
  • Add automatic reference counting as standard functionality, rather than leaving it to be the responsibility of the owner of the HandleMgr.
  • Add support for constant-time iteration over the potentially sparse object store by embedding a linked list within its elements. Use STL style iterator naming and operation for consistency.
  • Many databases, such as a font manager or texture manager, will likely require indexes to access objects by name to retrieve handles. Build this in as a standard feature or as a separate (derivative) class.
  • The HandleMgr system is especially effective when combined with the Singleton pattern (see “An Automatic Singleton Utility” elsewhere in this book). Many of a game’s databases are naturally singletons.
  • Take the Singleton pattern a little further, and make the TAG type of Handle actually be the type that it corresponds to within the HandleMgr. Then the Handle could have an operator -> that would dereference itself into a TAG by directly accessing the Singleton that manages it.
  • Save game functionality should be fairly easy to add, but it is necessarily specific to your game’s architecture. The handles can be saved out directly – just make sure that the HandleMgr stores the indexes for its objects along with the object data, and on restore, all handles will remain valid.

Listing 1 – Handle

#include <cassert>
 
template <typename TAG>
class Handle
{
    union
    {
        enum
        {
            // sizes to use for bit fields
            MAX_BITS_INDEX = 16,
            MAX_BITS_MAGIC = 16,
 
            // sizes to compare against for asserting dereferences
            MAX_INDEX = ( 1 << MAX_BITS_INDEX) - 1,
            MAX_MAGIC = ( 1 << MAX_BITS_MAGIC) - 1,
        };
 
        struct
        {
            unsigned m_Index : MAX_BITS_INDEX;  // index into resource array
            unsigned m_Magic : MAX_BITS_MAGIC;  // magic number to check
        };
        unsigned int m_Handle;
    };
 
public:
 
// Lifetime.
 
    Handle( void ) : m_Handle( 0 )  {  }
 
    void Init( unsigned int index );
 
// Query.
 
    unsigned int GetIndex ( void ) const  return (  m_Index  );  }
    unsigned int GetMagic ( void ) const  return (  m_Magic  );  }
    unsigned int GetHandle( void ) const  return (  m_Handle );  }
    bool         IsNull   ( void ) const  return ( !m_Handle );  }
 
    operator unsigned int ( void ) const  return (  m_Handle );  }
};
 
template <typename TAG>
void Handle <TAG> :: Init( unsigned int index )
{
    assert( IsNull() );             // don't allow reassignment
    assert( index <= MAX_INDEX );   // verify range
 
    static unsigned int s_AutoMagic = 0;
    if ( ++s_AutoMagic > MAX_MAGIC )
    {
        s_AutoMagic = 1;    // 0 is used for "null handle"
    }
 
    m_Index = index;
    m_Magic = s_AutoMagic;
}
 
template <typename TAG>
inline bool operator != ( Handle <TAG> l, Handle <TAG> r )
    return ( l.GetHandle() != r.GetHandle() );  }
 
template <typename TAG>
inline bool operator == ( Handle <TAG> l, Handle <TAG> r )
    return ( l.GetHandle() == r.GetHandle() );  }

Listing 2 – HandleMgr

#include <vector>
#include <cassert>
 
template <typename DATA, typename HANDLE>
class HandleMgr
{
private:
    // private types
    typedef std::vector <DATA>         UserVec;
    typedef std::vector <unsigned int> MagicVec;
    typedef std::vector <unsigned int> FreeVec;
 
    // private data
    UserVec  m_UserData;     // data we're going to get to
    MagicVec m_MagicNumbers; // corresponding magic numbers
    FreeVec  m_FreeSlots;    // keeps track of free slots in the db
 
public:
 
// Lifetime.
 
    HandleMgr( void )  {  }
   ~HandleMgr( void )  {  }
 
// Handle methods.
 
    // acquisition
    DATA* Acquire( HANDLE& handle );
    void  Release( HANDLE  handle );
 
    // dereferencing
    DATA*       Dereference( HANDLE handle );
    const DATA* Dereference( HANDLE handle ) const;
 
    // other query
    unsigned int GetUsedHandleCount( void ) const
        return ( m_MagicNumbers.size() - m_FreeSlots.size() );  }
    bool HasUsedHandles( void ) const
        return ( !!GetUsedHandleCount() );  }
};
 
template <typename DATA, typename HANDLE>
DATA* HandleMgr <DATA, HANDLE> :: Acquire( HANDLE& handle )
{
    // if free list is empty, add a new one otherwise use first one found
 
    unsigned int index;
    if ( m_FreeSlots.empty() )
    {
        index = m_MagicNumbers.size();
        handle.Init( index );
        m_UserData.push_back( DATA() );
        m_MagicNumbers.push_back( handle.GetMagic() );
    }
    else
    {
        index = m_FreeSlots.back();
        handle.Init( index );
        m_FreeSlots.pop_back();
        m_MagicNumbers[ index ] = handle.GetMagic();
    }
    return ( m_UserData.begin() + index );
}
 
template <typename DATA, typename HANDLE>
void HandleMgr <DATA, HANDLE> :: Release( HANDLE handle )
{
    // which one?
    unsigned int index = handle.GetIndex();
 
    // make sure it's valid
    assert( index < m_UserData.size() );
    assert( m_MagicNumbers[ index ] == handle.GetMagic() );
 
    // ok remove it - tag as unused and add to free list
    m_MagicNumbers[ index ] = 0;
    m_FreeSlots.push_back( index );
}
 
template <typename DATA, typename HANDLE>
inline DATA* HandleMgr <DATA, HANDLE>
:: Dereference( HANDLE handle )
{
    if ( handle.IsNull() )  return ( 0 );
 
    // check handle validity - $ this check can be removed for speed
    // if you can assume all handle references are always valid.
    unsigned int index = handle.GetIndex();
    if (   ( index >= m_UserData.size() )
        || ( m_MagicNumbers[ index ] != handle.GetMagic() ) )
    {
        // no good! invalid handle == client programming error
        assert( 0 );
        return ( 0 );
    }
 
    return ( m_UserData.begin() + index );
}
 
template <typename DATA, typename HANDLE>
inline const DATA* HandleMgr <DATA, HANDLE>
:: Dereference( HANDLE handle ) const
{
    // this lazy cast is ok - non-const version does not modify anything
    typedef HandleMgr <DATA, HANDLE> ThisType;
    return ( const_cast <ThisType*> ( this )->Dereference( handle ) );
}

Listing 3 – Sample Managed Class

#include <vector>
#include <map>
#include <cassert>
 
// ... [ platform-specific surface handle type here ]
typedef LPDIRECTDRAWSURFACE7 OsHandle;
 
struct tagTexture  {  };
typedef Handle <tagTexture> HTexture;
 
class TextureMgr
{
 
// Texture object data and db.
 
    struct Texture
    {
        typedef std::vector <OsHandle> HandleVec;
 
        std::string  m_Name;        // for reconstruction
        unsigned int m_Width;       // mip 0 width
        unsigned int m_Height;      // mip 1 width
        HandleVec    m_Handles;     // handles to mip surfaces
 
        OsHandle GetOsHandle( unsigned int mip ) const
        {
            assert( mip < m_Handles.size() );
            return ( m_Handles[ mip ] );
        }
 
        bool Load  ( const std::string& name );
        void Unload( void );
    };
 
    typedef HandleMgr <Texture, HTexture> HTextureMgr;
 
// Index by name into db.
 
    // case-insensitive string comparison predicate
    struct istring_less
    {
        bool operator () ( const std::string& l, const std::string& r ) const
            return ( ::stricmp( l.c_str(), r.c_str() ) < 0 );  }
    };
 
    typedef std::map <std::string, HTexture, istring_less > NameIndex;
    typedef std::pair <NameIndex::iterator, bool> NameIndexInsertRc;
 
// Private data.
 
    HTextureMgr m_Textures;
    NameIndex   m_NameIndex;
 
public:
 
// Lifetime.
 
    TextureMgr( void )  {  /* ... */  }
   ~TextureMgr( void );
 
// Texture management.
 
    HTexture GetTexture   ( const char* name );
    void     DeleteTexture( HTexture htex );
 
// Texture query.
 
    const std::string& GetName( HTexture htex ) const
        return ( m_Textures.Dereference( htex )->m_Name );  }
    int GetWidth( HTexture htex ) const
        return ( m_Textures.Dereference( htex )->m_Width );  }
    int GetHeight( HTexture htex ) const
        return ( m_Textures.Dereference( htex )->m_Height );  }
    OsHandle GetTexture( HTexture htex, unsigned int mip = 0 ) const
        return ( m_Textures.Dereference( htex )->GetOsHandle( mip ) );  }
};
 
TextureMgr :: ~TextureMgr( void )
{
    // release all our remaining textures before we go
    NameIndex::iterator i, begin = m_NameIndex.begin(), end = m_NameIndex.end();
    for ( i = begin ; i != end ; ++i )
    {
        m_Textures.Dereference( i->second )->Unload();
    }
}
 
HTexture TextureMgr :: GetTexture( const char* name )
{
    // insert/find
    NameIndexInsertRc rc =
        m_NameIndex.insert( std::make_pair( name, HTexture() ) );
    if ( rc.second )
    {
        // this is a new insertion
        Texture* tex = m_Textures.Acquire( rc.first->second );
        if ( !tex->Load( rc.first->first ) )
        {
            DeleteTexture( rc.first->second );
            rc.first->second = HTexture();
        }
    }
    return ( rc.first->second );
}
 
void TextureMgr :: DeleteTexture( HTexture htex )
{
    Texture* tex = m_Textures.Dereference( htex );
    if ( tex != 0 )
    {
        // delete from index
        m_NameIndex.erase( m_NameIndex.find( tex->m_Name ) );
 
        // delete from db
        tex->Unload();
        m_Textures.Release( htex );
    }
}
 
bool TextureMgr::Texture :: Load( const std::string& name )
{
    m_Name = name;
    // ... [ load texture from file system, return false on failure ]
    return ( true /* or false on error */ );
}
 
void TextureMgr::Texture :: Unload( void )
{
    m_Name.erase();
    // ... [ free up mip surfaces ]
    m_Handles.clear();
}
Posted in Uncategorized | Leave a comment

My life rating Aug 2010

This Is My Life, Rated
Life: 5.5
Mind: 5.1
Body: 8.2
Spirit: 5.5
Friends/Family: 1.2
Love: 4.6
Finance: 6.8
Take the Rate My Life Quiz
Posted in Uncategorized | Leave a comment

facebook 升级了, 被赶回来了 哈哈.
 
很久没写东西了 过去一段很长的时间一个字就能概括: 忙
忙得没时间休息,
忙得没时间陪女友逛街,
忙得没时间跟老妈说话,
忙得没时间运动,
忙着忙着该学的东西都学会了,

软件一个个出去了,

每天给足了110%的努力,没有什么后悔的地方了.

忙着忙着突然有一天顿悟, 是时候离开这个变态的公司了.
择善而居,有益身心健康.
 
 到处发了简历, 突然收到游戏公司一个电话, 面试了,offer 来了, 签了offer发回去了,
把原来公司的假期放完了, 过了个好年然后辞职, 一切都还挺顺利的.
 
放假的时候,准备去换地址 结果顺便把Advance 路试考了.
 又顿悟了…
东西经常做就熟悉了, 自然轻松了,写软件是那样,工作是那样,开车是那样.
一切就这样通过了.
 
 
 
Posted in Uncategorized | 10 Comments

Windows Live is too damn confusing

After months of denying the fact that the updates in windows live sux, I finally give up.
It takes too many clicks to just update my blog or upload a picture.

And what’s up with those random messages from people I don’t even know.

Goodbye Windows live space.
Hello facebook.

You know where to find me 😉

 

Posted in Uncategorized | 6 Comments

大年初一 答辩过了

从赶论文到最后答辩 刚好横跨 圣诞, 元旦 和新年.  答辩那天中午 还有人跟我说
大年初一答辩 那怎么过年啊. 我心里一阵汗 看来整个过程就是不要过年过节 拼毕业 -_-!
 
论文从原来担心写不过50 页 到最后挤进去很多有的没的内容 真是一个磨练的过程.
过程中有时很开心 知道自己在搞啥 有时很不爽, 都不知道自己搞什么冬冬.
还好有家人女友一路的支持.
对待牛人老板不停扩大的 scope.
俺感觉跟最近那个电影 Yes Man 很像. 改到疯狂的阶段, 一个晚上大改一次.
在论文交去审查的前一个晚上 还在改. 老板在msn 上 讨论了一天. 然后通宵改 他去睡觉 我重新做试验, 改论文.
累得我都要吐血了. 现在回头看来 改的那个东西好像是论文最大的贡献, 人生有时也挺神奇的.(嗯 当归也是很神奇的)
 
答辩的过程 跟传言的一样 有人会问难的问题.
结果传言中会问难问题的那个教授对我超好 好像一开始就说了句 论文都看懂了 然后问的问题超简单. (嗯难道是 过年开恩了 哈哈)
我老板的问题都很难 我一听就喷饭了, 然后俺脑袋就像计算机一样在那转啊转啊.
还好他之前也经常这样 天花乱坠的问问题.  有2题没有答出来, 最简单的最后一题反而被吓到了
结果看过也忘记了. 就这样 教授们都累了 也没怎么讨论, 俺喝了口水 去了个厕所就过了.
在一瞬间 大家都签好名 握了握手 恭贺了2句 就不见了. 结果老板 留下来 跟我布置 怎样重做试验
图上要加什么冬冬. 汗啊.
 
接下来可以开心回国享受几天过年的尾巴再回来加入找工作大军 哈哈 
 
 
 
Posted in Uncategorized | Leave a comment

Happy new year

原来笑话也不能随便转 呵呵
Posted in Uncategorized | 2 Comments

Still a Norton fan

What  do i got for boxing day? a nasty virus.
 
Phew~~~ Norton 2009 saves all my precious precious crap writings.
 
It is still slow still have that long disk spins but hey, it works.
Stupid windows popup disappeared.
 
Kapersky is not as good .(it’s just my own opinion though)
The trial version fails, then database corrupted on update.
At last it fails to detect that one virus i want to get rid of.
 
Going back to work.
Posted in Uncategorized | Leave a comment

金鱼活回来了 哈哈

MoMo 泡了5天药 康复了
开心
原来鱼的鳍会自己长回来 厉害
为什么我的脚指甲不能长那么快呢 哈哈
老妈说要拔掉 汗啊~~~
 
第二天: 指甲被女友完完整整的踩掉了 晕
Posted in Uncategorized | Leave a comment