• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            2011年12月11日

            C++ library series -- in the MFC multiple-thread environment, how to quit worker-thread safely which begins with AfxBeginThread

              In the MFC environment, normally, thread should be launched with AfxBeginThread for taking usage of MFC multiple-thread mechanism; In such mechanism, those datastructures, such as AFX_MODULE_STATE, would be used by MFC framework to maintain related thread information. It runs well when threads, launched with AfxBeginThread, quit before the main thread, which is responsible for initializing C run-time, but if such main thread quit before any other thread launched by AfxBeginThread, the current application would crash.
              Such crash comes from the _afxThreadData (CThreadSlotData* _afxThreadData, which is defined in AFXTLS.cpp as global data structure) has been destructed while the main thread quits and it will invoke related function to clean up global data structures, including _afxThreadData definitely.
              Consequently, serious developer should prepare for such case (other worker thread quits before main thread).
              
              The reasonable resolve for such issue, would ensure any other threads should quit before the main thread. 
              
            .h file 
              /////////////////////////////////////////////////////////////////////////////
            // CSafeEnterLeaveThread thread
            class CSafeEnterLeaveThread : public CWinThread
            {
            DECLARE_DYNCREATE(CSafeEnterLeaveThread)
            protected:
            CSafeEnterLeaveThread();           // protected constructor used by dynamic creation
            // Attributes
            public:
            // Operations
            public:
            // Overrides
            // ClassWizard generated virtual function overrides
            //{{AFX_VIRTUAL(CSafeEnterLeaveThread)
            public:
            virtual BOOL InitInstance();
            virtual int ExitInstance();
            //}}AFX_VIRTUAL
            // Implementation
            protected:
            virtual ~CSafeEnterLeaveThread();
            // Generated message map functions
            //{{AFX_MSG(CSafeEnterLeaveThread)
            // NOTE - the ClassWizard will add and remove member functions here.
            //}}AFX_MSG
            DECLARE_MESSAGE_MAP()
            };
            .cpp file 
            /////////////////////////////////////////////////////////////////////////////
            // CSafeEnterLeaveThread
            IMPLEMENT_DYNCREATE(CSafeEnterLeaveThread, CWinThread)
            CSafeEnterLeaveThread::CSafeEnterLeaveThread()
            {
            }
            CSafeEnterLeaveThread::~CSafeEnterLeaveThread()
            {
            }
            BOOL CSafeEnterLeaveThread::InitInstance()
            {
            // TODO:  perform and per-thread initialization here
            ASSERT(this->m_hThread);
            CMainApp::RegisterMFCThread(this->m_hThread);
            return TRUE;
            }
            int CSafeEnterLeaveThread::ExitInstance()
            {
            // TODO:  perform any per-thread cleanup here
            ASSERT(this->m_hThread);
            CMainApp::UnRegisterMFCThread(this->m_hThread);
            return CWinThread::ExitInstance();
            }
            BEGIN_MESSAGE_MAP(CSafeEnterLeaveThread, CWinThread)
            //{{AFX_MSG_MAP(CSafeEnterLeaveThread)
            // NOTE - the ClassWizard will add and remove mapping macros here.
            //}}AFX_MSG_MAP
            END_MESSAGE_MAP()
            And in the CMainApp,
            set<HANDLE> g_ThreadHandleSet;
            HANDLE g_ThreadHandleArray[MAXIMUM_WAIT_OBJECTS];
            CCriticalSection g_csGlobalData;
            void CAccgbApp::CheckAllOtherMFCThreadsLeave()
            {
            int count = g_ThreadHandleSet.size();
            if (count == 0) return;
            set<HANDLE>::iterator it;
            int idx = 0;
            for (it = g_ThreadHandleSet.begin(); it != g_ThreadHandleSet.end() && idx < MAXIMUM_WAIT_OBJECTS; it++, idx++)
            {
            g_ThreadHandleArray[idx] = *it;
            }
            if (count > idx) count = idx;
            ::WaitForMultipleObjects(count, g_ThreadHandleArray, TRUE, INFINITE);
            }
            void CAccgbApp::CleanupGlobalData()
            {
            g_csGlobalData.Lock();
            g_ThreadHandleSet.empty();
            g_csGlobalData.Unlock();
            }
            BOOL CAccgbApp::RegisterMFCThread(HANDLE hThread)
            {
            if (hThread == NULL) return FALSE;
            g_csGlobalData.Lock();
            if (g_ThreadHandleSet.find(hThread) == g_ThreadHandleSet.end()) 
            g_ThreadHandleSet.insert(hThread);
            g_csGlobalData.Unlock();
            return TRUE;
            }
            void CAccgbApp::UnRegisterMFCThread(HANDLE hThread)
            {
            if (hThread == NULL) return;
            g_csGlobalData.Lock();
            if (g_ThreadHandleSet.find(hThread) != g_ThreadHandleSet.end())
            g_ThreadHandleSet.erase(hThread);
            g_csGlobalData.Unlock();
            }

            posted @ 2011-12-11 20:35 flagman 閱讀(1673) | 評論 (0)編輯 收藏

            2011年4月4日

            操作系統怎么根據一個HWND句柄,找到相應的代碼

            【 在 某 的大作中提到: 】

            : 比如我有一個CMyButton的類,我現在有他的一個handle
            : 編譯器怎么根據這個句柄找到CMyButton的代碼的?

            【 在 某某 的大作中提到: 】
            : 這個和OS/Compiler沒關系,是庫起的作用
            : 以從某個文章里看的,說MFC用了一個大map,沒驗證過
            : 有本講GDI的書里,用了WNDCLASS里的extra bytes來實現的這個映射

             


            MFC的應用里,每個MFC線程(必須要使用MFC方式啟動的線程)都維護有一個MFC object和HWND之間的

            mapping,整個MFC框架就是使用這個機制來實現應用級C++對象和系統級原生窗口內核對象之間的關聯;

            因為這個mapping是以線程為單位來維護的,每個線程間互不關聯,所以,一個應用里對于涉及UI窗口的

            任務最好是都放在同一個線程里面,一般就是當前進程的主線程,否則可能出現MFC object和HWND之間

            關聯不上的問題,而且這樣的問題還很隱蔽。

            至于WNDCLASS結構自帶的extra bytes域,是以前缺乏應用框架的時代,使用Win32 API直接開發時,讓每個

            窗口類(這里的類,不是C++ class的概念,而是Windows系統窗口定義時的一種數據結構)都能有個附

            帶一些額外的自定義數據的空間,這個空間往往被用來存放與當前窗口類相關的用戶數據,通常是指向

            某個內存區域的指針,當程序操作這個屬于這個窗口類的窗口時就可以根據這個附帶的自定義數據(或

            者指針)來操作對應的關聯自定義數據;很多后來出現的框架,也都使用了這個extra bytes域,來存放

            框架本身的一些和窗口類相關聯的數據結構。從目前趨勢看,直接使用WNDCLASS以及extra bytes的可能

            性是微乎其微了,但是如果要做好原生應用的開發,很多底層的實現細節最要還是要知道一下,以便于

            優化結構和性能,以及出錯時的調試處理;因為無論是Winform/WPF,還是跨平臺的WTL/QT/WxWindows等

            等新型的機制或者框架、類庫,只要是在Windows平臺上搭建的,那都是基于前面說過的這套最基本也是

            最核心的Win32 API基礎之上。

             

            posted @ 2011-04-04 14:16 flagman 閱讀(1731) | 評論 (4)編輯 收藏

            2011年2月27日

            TC SRM144DIV2 千分題題解

            這道千分題,其實是挺有意思的一題:
            提供了點(這里是junction)和點之間的距離(或代價);
            要求以最短的距離(或最小代價)遍歷所有的點,同時每個點可以多次訪問;

            初看之下,給人的感覺是圖論相關的問題,比如旅行者問題、歐拉環游之類。

            在思考這個問題的時候,忽然間聯想到了圖論中的最小生成樹,雖然并不是真正要去得出最小生成樹,

            但是按照最小生成樹所提供的思路--這點很重要--那就是圖和樹之間有著相當密切的關系:即使最小生

            成樹并不能直接解決這個問題,但是它們之間存在的這層關系的確提供了解決問題的一個有益的嘗試方

            向;

            于是,思考進了一步,問題從“圖”簡化成了“樹”--如何把當前這個問題采用樹的結構和方法表達出

            來:樹的根節點,很自然地想到了由問題中旅行的起始節點來表達;然后,隨著節點的不斷加入,樹就

            自然地生成,此處的關鍵在于如何生成,或者說節點加入的規則,以及每個節點為了適應這個規則,所

            必須持有的相關屬性信息:最直接的,父子節點之間的關系需要維護,從父節點到子節點的距離(或代

            價)必須要保留,其次,考慮到如果每個節點都維護相關的距離(代價)信息,那么從當前節點到根節

            點的代價也就可以由此遞推得出,進一步,我們所要求出的最短路徑(或最小代價)不就可以從上述這

            些節點中維護的距離信息中得出嗎?這是非常關鍵的一步,它把當前我們構建的數據結構和問題的要求

            之間建立起了相當直接的聯系。這說明我們目前思考的方向是有價值的而且極有可能順著這個方向前行

            ,可以得出相當不錯的結果。

            顯然,既然要求最短路徑(最小代價),那么我們目前構建出的這顆Junction樹(因為其上的節點在題

            中的物理含義是代表Junction,那這里我們就姑且稱其為Junction Tree),樹上的每個節點也應當保留

            在其之下的子樹的最短路徑(最小代價),這就相當于把每個節點都作為根節點,然后求出各條子路徑

            的代價,并比較得出最短路徑(最小代價),以及在這條最短路徑上的直接子節點;

            每加入一個子節點,就要對上述已構建出的這些數據結構中的信息進行維護,以調整每個節點當前的最

            短路徑代價和相應這條路徑上的直接子節點;當所有原“圖”中的“邊”信息,也就是

            (fromJunction,toJuction,ductLength)所代表的(起始點,終止點,長度代價),都按照上述方案加入

            Juction Tree之后,我們可以知道從最初的起始節點(也就是Junction Tree的根節點)到最終節點的(

            Junction Tree上的某條路徑上的葉子節點)的最短(最小代價)路徑了。

            對于Juction Tree這個ADT抽象數據結構的具體實現,考慮到優先隊列中二叉堆的經典實現往往使用數組

            ,同時也為了符合TC SRM一貫的簡捷明快的程序設計風格,我們這里同時使用幾個數組來維護前述構建

            出的數據結構。

            //////////////////////////////////////////////////////////////////////////////////////////

            #include<cstdlib>
            #include<vector>
            #include<set>
            using namespace std;

            const int NIL = -1;
            const int MAX = 50;

            int Cost[MAX];
            int ParentNode[MAX];
            int MaxSubNode[MAX];
            int MaxSubCost[MAX];

            class PowerOutage
            {
            public:
             int estimateTimeOut(vector<int> fromJunction, vector<int> toJunction, vector<int>

            ductLength)
             {
              if (!CheckParameter(fromJunction, toJunction, ductLength)) return NIL;
              
              Ini();
              int count = fromJunction.size();
              for (int i = 0; i < count; i++)
              {
               AddNode(fromJunction[i], toJunction[i], ductLength[i]);
              }
              
              return CalculateMinCost(fromJunction, toJunction, ductLength);
             }
            private:
             void Ini()
             {
              memset(Cost, NIL, sizeof(int) * MAX);
              memset(ParentNode, NIL, sizeof(int) * MAX);
              memset(MaxSubNode, NIL, sizeof(int) * MAX);
              memset(MaxSubCost, 0, sizeof(int) * MAX);
             }
             
             bool CheckParameter(const vector<int>& fromJunction, const vector<int>& toJunction,

            const vector<int>& ductLength)
             {
              if (fromJunction.size() != toJunction.size() || toJunction.size() !=

            ductLength.size())
               return false;
              return true;
             }
             
             void AddNode(int parent, int child, int cost)
             {
              if (parent < 0 || child < 0 || cost < 0) return;
              
              Cost[child] = cost;
              ParentNode[child] = parent;
              
              int curParent = parent, curChild = child;
              bool adjustParentCost = true;
              while (adjustParentCost && curParent != NIL)
              {
               int candidateParentMaxSubCost = Cost[curChild] + MaxSubCost

            [curChild];
               if (MaxSubCost[curParent] < candidateParentMaxSubCost)
               {
                MaxSubCost[curParent] = candidateParentMaxSubCost;
                MaxSubNode[curParent] = curChild;
                
                curChild = curParent;
                curParent = ParentNode[curParent];
               }
               else
               {
                adjustParentCost = false;
               }
              }
             }
             
             int CalculateMinCost(const vector<int>& fromJunction, const vector<int>&

            toJunction, const vector<int>& ductLength)
             {
              int len = fromJunction.size();
              int minCost = 0;
              
              set<int> minCostPath;
              minCostPath.insert(0);
              int curNode = MaxSubNode[0];
              while(curNode != NIL)
              {
               printf("%d;",curNode); // print the min cost path
               
               minCostPath.insert(curNode);
               curNode = MaxSubNode[curNode];
              }
              
              for (int i = 0; i < len; i++)
              {
               if (minCostPath.find(toJunction[i]) == minCostPath.end())
                minCost += 2 * ductLength[i];
               else
                minCost += ductLength[i];
              }
              
              return minCost;
             }
            };

            posted @ 2011-02-27 17:09 flagman 閱讀(1606) | 評論 (0)編輯 收藏

            2011年2月12日

            反射的特性是經常會使用到的

            【 某網友討論道: 】
            : RT,反射的特性發現很少用啊



            恰恰相反,有些反射的特性是經常會被使用到的。

            反射總體上分成兩大特性,一是自省,二是發射;

            自省的能力極為重要,而且幾乎會天天用到,很少見到過哪個.net應用中不使用attribute的,而attribute特性就是metadata通過在自省能力支撐下實現的;當然自省不單單是attribute特性的運用,只要是在運行時動態檢視程序自身的特性都要由反射的自省能力來支持,比如Visual Studio的IDE(這個集成開發環境本身就是.net應用的好案例)對于.net組件的自動探測功能;同時,自省的能力也是基于虛擬機平臺的語言,比如c#和java,區別于傳統語言比如c和c++的重要特性之一,這提供了程序設計開發更為便利和安全的運行時環境;相對而言,在c++(當然是native而不是managed)的環境下,除了RTTI極為單薄的運行時自省,也就是QT這個庫通過meta-object system部分模擬了自省的特性;

            反射的另外一個重要特性就是發射,它讓“程序可以寫程序”了,簡要的說就是在運行時動態生成MSIL并加載運行以及持久化動態生成的MSIL的能力;由這個特性的支持,讓原先一些程序設計和開發領域相對困難和繁瑣的工作,比如元編程meta programming,比如動態代理dynamic proxy,比如AOP中的基礎設施weaver的實現,變得可能或相對易于實現;反射的特性,也是基于虛擬機平臺CLR的支持,以metadata為基礎來實現的,所以這也是虛擬機平臺語言的特有優勢,而在傳統語言平臺上,這是難以實現的;比如關于meta programming,c++就是通過模板特性實現的編譯期meta programming,這與虛擬機平臺上實現的運行時meta programming還是有比較大的差距(比如前者如何保證生成的代碼的type-safe);

            以上這兩個特性,自省和發射,都有個共同點,他們都是圍繞著metadata機制,并在虛擬機平臺運行時環境CLR支持下實現的,前者是運行時檢視相關的metadata,后者是運行時動態生成相關的metadata和MSIL;從這點也就可以看出,要想深入理解這些特性,就需要研究metadata和MSIL的實現,以及虛擬機運行時環境的實現(在java平臺上,就是bytecode和JVM);

            所以,反射,可能是虛擬機平臺所提供的相對最為強勁,最為復雜,和平臺運行時本身關系最密切,也是區別于傳統語言和運行時最鮮明的特性。

            posted @ 2011-02-12 17:21 flagman 閱讀(2134) | 評論 (1)編輯 收藏

            2011年2月8日

            C++ library系列 -- static destructors in multiple threads

              In VC++ 8.0, while  code compiled with /clr or /clr:pure, static destructors sometimes would not being properly called before process exites in multiple threads.

              CRT incorrectly set a lock at _global_unlock which resulted in such issue.

              In CLR-mixed mode, during the inialization of static local object, CRT would call _atexit_m(_CPVFV func) in msilexit.cpp to register a special __clrcall callback function which would be called back to destroy such static object when the current AppDomain quited.

              In the multithread environment, _atexit_helper which was invoked by _atexit_m, could register such callbace function successfully because it had been guarded by __global_lock() and __global_unlock(). But in the same environment, the _atexit_m would fail to assign the correct value to __onexitbegin_m and __onexitend_m.

              __onexitbegin_m and __onexitend_m were shared by the different threads; It's the key point of such issue. For example, the following statements,

              __onexitbegin_m = (_CPVFV *)_encode_pointer(onexitbegin_m);
              __onexitend_m = (_CPVFV *)_encode_pointer(onexitend_m);

            should also guarded by __global_lock() and __global_unlock() or other syn primitives.


            __global_lock();
            __onexitbegin_m = (_CPVFV *)_encode_pointer(onexitbegin_m);
            __onexitend_m   = (_CPVFV *)_encode_pointer(onexitend_m);
            __global_unlock();


            extern "C" int __clrcall _atexit_m(_CPVFV func)
            {
             MANAGED_ASSERT(AppDomain::CurrentDomain->IsDefaultAppDomain(), "This fuction must be called in the default domain");

             __global_lock();
             _CPVFV* onexitbegin_m = (_CPVFV*)_decode_pointer(__onexitbegin_m);
             _CPVFV* onexitend_m = (_CPVFV*)_decode_pointer(__onexitend_m);
             __global_unlock();

             int retval = _atexit_helper((_CPVFV)_encode_pointer(func), &__exit_list_size, &onexitend_m, &onexitbegin_m);

             __global_lock();
             __onexitbegin_m = (_CPVFV*)_encode_pointer(onexitbegin_m);
             __onexitend_m  = (_CPVFV*)_encode_pointer(onexitend_m);
             __global_unlock();

             return retval;
            }

            posted @ 2011-02-08 20:57 flagman 閱讀(2099) | 評論 (0)編輯 收藏

            2010年12月19日

            C++ library系列 -- STL實現中的ODR “one-definition-rule” for types

            Linking issue
            - While different modules (.obj) using istreambuf_iterator/ostreambuf_iterator, compiled with different options on HID/no-HID and SCL/no-SCL, these modules could not be linked successfully;

            The error comes directly from the CLR when a type has multiple definitions that are not consistent based upon the ODR, one-definition-rule for types. And, the linker itself isn't involved.

            For example, with one module compile with /D_SECURE_SCL=0, while another is compiled with _SECURE_SCL=1;

            At first, it's found that with _SECURE_SCL, the only thing that could be different as following,

            #if _SECURE_SCL
                typedef _Range_checked_iterator_tag _Checked_iterator_category;
            #endif

            But, actually, it's not the typedef that changed the layout the these iterators (istreambuf_iterator/ostreambuf_iterator), and further they don't really use the extra pointer that _SECURE_SCL adds.

            Finally, it's found the root cause is that, these iterators, istreambuf_iterator/ostreambuf_iterator  had been moved from <xutility> to <streambuf>, and their ultimate base class had been changed from _Iterator_base_secure to _Iterator_base. And, the layout of _Iterator_base would be different between HID and no-HID, and between SCL and no-SCL. It is the cause where the issue comes from.

            What we can learn from such issue,
            These iterators really shouldn't derive from either _Iterator_base_secure or _Iterator_base, because these classes contain data members (pointers) which are entirely unused. It would result in unnecessary bloat and extra work being performed in ctor/dtor etc.

            Introduce a new class, _Iterator_base_universal, which is defined identically regardless of HID/no-HID and SCL/no-SCL. It would contains the three internal typedefs that all standard iterators need to have, and nothing else. And _Iterator_base (in all of its variants) and _Iterator_base_secure now should derive from _Iterator_base_universal to get these typedefs.

            Now, when an iterator wants these typedefs, but not the data members of _Iterator_base and _Iterator_base_secure, it could derive from _Iterator_base_universal. And istreambuf_iterator and ostreambuf_iterator are now as small as possible, and keep identical representations or layout across HID/no-HID, SCL/no-SCL.

            posted @ 2010-12-19 11:09 flagman 閱讀(1894) | 評論 (0)編輯 收藏

            關于COM和.net的思考

            【 某某提到: 】
            : 一般說COM復雜,首先是名詞太多,其次是基于ATL的實現比較難懂
            : 這并不是COM本身復雜,而是C++已經落后于時代了。所以ATL看起來才會像天書一般


            雖然對于全新的工程項目,推薦通過.net實現,但是,只要你工作在Windows平臺上,必然會遇到和COM相關的技術和機制,無論是大量的legacy的工程和代碼,還是作為OS重要功能以及native組件的首選交互形式和接口暴露方式,比如DirectX API,比如一些WMI的API;最有趣的是,即使是.net的核心CLR本身也是一個COM組件,可以通過Host相關接口讓native應用來加載,以在當前進程中啟動整個CLR的虛擬執行環境或者叫托管執行環境(managed executive environment)。

            把握COM有兩點很關鍵,
            1)Interface-based design,從設計和編碼思路上就是要完全基于接口;
            2)VirtualTable-based binary compatibility, 實現上無論何種語言或者機制,只要符合基于虛表的二進制兼容規范,就都可以實施;

            COM僅僅是個規范,基于COM的具體技術非常之多,OLE,Automation,Structural storage,ActiveX...汗牛充棟,還有COM+,這個是提供企業級開發必備的一些基礎功能和設施,比如,事務管理機制,對象池,安全管理,消息隊列...需要指出,目前即便是.net Framework也沒有實現COM+所提供這些機制,只是簡單的封裝了后者。

            COM技術中可能有一些比較困難的地方,接口的一致性,對象的聚合和生命周期,套間,跨套間的接口訪問,名字對象,等等;這些并不是COM規范人為制造的困難,而是為了設計和提供,可以跨進程和機器邊界,跨異構平臺(當然必須實現了COM所規定的基礎服務),透明化具體對象類型及對象生命周期,便于統一部署和版本管理的組件技術,所必須付出的代價,這個代價從開發人員角度看具體表現為,概念理解的困難以及具體二進制實現的困難;

            不過從另一個角度看,COM已經很容易了,
            a) COM規范已把要達致這些目標的系統,所必須提供的接口和特性抽象了出來,只不過為了表達這些抽象的概念而新造的術語名詞有些陌生和突兀;如果讓遇到相似問題的每一個設計和開發人員都自己來做抽象,未必會生成更好的方案;

            b) 為了幫助設計和開發人員,人們提供了很多的開發庫,以提高COM開發的正確性和效率;最顯著的就是MFC中關于COM/OLE的輔助類和函數,以及為了COM而生的ATL;從本質上看,這些類庫都是把COM規范中必須實現的,Windows平臺本身沒有提供,具體設計和開發人員實際實施時會重復實現的,同時又非常容易出錯的那部分功能,集中到了這些類庫里統一實現,讓具體設計和開發人員以代碼重用的形式來實現COM規范;

            當然人們也意識到了COM這樣的一些問題,特別是具體實現時設計和開發人員必須要關注幾乎所有的二進制細節,于是.net就誕生了,把這些規范的許多復雜性都封裝在了虛擬機里面,把這些目標功能(跨邊界、透明性等等)通過一致而又平滑的平臺接口和自描述的meta data,以一種讓設計和開發人員更易接受的風格開放了出來;

            COM的影響是非常廣大的,比如XPCOM ,Firefox上的一種插件技術標準,就是根據COM的思想和原則制定的;許多評論說,Firefox的成功是因為它插件是如此的成功,這也算是COM本身所意料不到的貢獻之一。

            在.net的平臺上,即使是.net CLR/SSCLI的具體實現也大量運用了COM的思想和機制,可以說.net就是搭建在COM二進制組件平臺之上的虛擬機托管平臺。

            最后,.net開始時的內部編號是COM 2.0

             

            ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

            *) 關于“名詞太多”
            這是要實現可以跨進程和機器邊界,跨異構平臺(當然必須實現了COM所規定的基礎服務),透明化具體對象類型及對象生命周期,便于統一部署和版本管理的組件技術,所必須付出的代價。

            COM規范已把要達致這些目標的系統,所必須提供的接口和特性抽象了出來,只不過為了表達這些抽象的概念而新造的術語名詞有些陌生和突兀;如果讓遇到相似問題的每一個設計和開發人員都自己來做抽象,未必會生成更好的方案;

            舉個例子,apartment,套間,就是為了抽象傳統OS中進程和線程的實現而新造的術語名詞和概念;任何人要抽象這樣的一些概念,不新造術語,是非常困難的,對比.net,后者用了CLR虛擬機來封裝了大多數的實現細節,并用讓人更容易接受的風格來開放接口,可事實上仍然新造了一些名詞和概念,如類似范疇的AppDomain;

            *) 關于“基于ATL的實現比較難懂”
            ATL主要使用了template技術,COM接口智能指針,用靜態轉換來模擬動態綁定,等等,實際并不是很復雜,只能算c++實現機制的中等難度,主要涉及Modern C++ design一書中一些相關設計理念的運用。對比Boost中某些庫的實現,ATL很人道了。

            *) 關于“這并不是COM本身復雜,而是C++已經落后于時代了”
            首先COM的規范的確是復雜的,為啥?第一點已經說了,就是為了要抽象出跨邊界和對象透明的組件技術;.net表象上看比較“簡單容易”,風格親近設計和開發人員,實際上復雜事務和實現細節都被劃分到CLR那個層面上去實現了;去看一下CLR的開源實現SSCLI,你會發現,整個虛擬機平臺的實現,大量運用了COM的思想和機制,就是一個巨型系統平臺級的COM server;

            其次,COM規范本身是獨立于實現語言的,只要構建出的組件符合規范制定的二進制兼容,系統就可以運作,這和C++是否落后時代沒有關系。如果開發人員認為,.net才夠先進,也完全可以用.net中的托管語言,如C#來實現COM組件;

            最后,每種語言都有其適用的范圍,現在可以這么說“如果有一個全新的項目需求,要達致跨邊界和對象透明組件,并且沒有太過嚴苛的性能需求,那么.net平臺及其上的托管語言來實現,比用C++及相關輔助類庫來以COM組件形式來實現,要更合適,也更快速便捷和節省預算。”但是,在這個判斷上我們加了很多嚴格的約束,一旦需求變更,特別是項目的非功能性需求,要求高性能運算或者更順暢的與legacy的native系統相互,那么“使用native語言來實現性能關鍵以及legacy交互功能,通過COM封裝,再由COMInterop交.net托管應用調用”可能是更現實的方案。C++是一門活的語言,不斷發展的語言,即使在最新的托管時代里,C#成為標準主流,但C++/CLI仍然是托管語言里功能最完整的語言。

             

            posted @ 2010-12-19 11:04 flagman 閱讀(2045) | 評論 (5)編輯 收藏

            2010年12月13日

            關于金山系列軟件開源代碼的思考

            金山系列軟件中的一部分代碼open source了,自然地引起網絡上以及IT業界的一片熱評。其中關于已經開源的這部分外圍代碼的代碼質量的問題,更是熱中之熱;以下是關于這個問題個人的一些思考:

            從本質上說這里面就是個trade off,也就是平衡和取舍的問題。產品項目的預算投入,進度壓力,各方面人員的協調,風格和習慣的統一,等等。

            許多優秀開源項目,比如Boost,其中很多作者本身都是學者兼開發或者是帶有研究性質的開發人員,在高校、非盈利組織或者商業企業的非直接盈利項目的資金支持下,在很少進度壓力和商業壓力的情況下,精雕細琢,多次迭代后,構建出的精品代碼。用這樣的標準來要求所有的軟件產品,特別是商業產品(當然除去少數關系重大和長遠的基礎核心部分外)的構建,是不科學的,也是不合算的,因為及時占領市場和足夠的盈利,以及獲得用戶的贊許才是商業軟件最重要的目標。

            回頭來看金山目前開源的這些產品,比如這里討論的金山衛士,其從推出就是免費的,是為了市場上的先期推出的同類工具軟件及時比拼占領些許相關市場份額,其并不是金山的基礎和核心產品;從這些先天的條件看,這個產品的商業投入不會很大同時又有快速推出的要求,能有目前這樣的產品質量,是合理的,從企業角度和用戶角度看也都是可以接受的。

            說到這里,就感覺有必要涉及一下“重構”,這個現在大家都很重視同時也經常談及的話題。為何大家都很重視?而且常常談及?這其中當然有軟件構建本身的特點,比如對需求理解的不斷深入和調整、設計的不斷改善和演進、代碼風格的統一以及細節的完善等等;但是,有個大家在潛意識里都感覺到,平時卻很少談及的緣由--進度和成本,因為有了這些壓力,產品的第一版往往不是很完美的,然后如果還做后續版本的話,那么就要引入重構;因為有了這些壓力,在經過多年之后,如果這個產品還存在的話,那么就要進行大規模的重構。簡單的說,重構之所以重要,不僅僅是軟件構建本身特點所引發,也是商業壓力之下的構建過程的有效應對之道。

            posted @ 2010-12-13 09:18 flagman 閱讀(2252) | 評論 (3)編輯 收藏

            CLR系列--探索SSCLI【1】

            Fusion is one of the most importants features among ones in the runtime implementation of CLI.

            In the fusion, or any other components or modules, how to retrieve the execution engine instance and how to generate such engine?

            UtilExecutionEngine, implemented as COM object, support Queryinterface/AddRef/Release, and exposed via interface IExecutionEngine.

            With SELF_NO_HOST defined,
            BYTE g_ExecutionEngineInstance[sizeof(UtilExecutionEngine)];
            g_ExecutionEngineInstance would be the singleton instance of current execution engine,

            otherwise, without SELF_NO_HOST, the 'sscoree' dll would be loaded and try to get the exported function, which is named 'IEE' from such dll. Here, it is the well-known shim, in .net CLR, such module is named 'mscoree'. Further, if 'IEE' could not be found in such dll, system would try to locate another exported function, named 'LoadLibraryShim', and use such function to load the 'mscorwks' module, and try to locate the 'IEE' exportd functionin it.

            It's very obvious that Rotor has implemented its own execution engine, but it also gives or make space for implementation of execution engine from 3rd party. Here, .net CLR is a good candidate definitely, Rotor might load the mscorwks.dll module for its usage.

            PAL, PALAPI, for example, HeapAlloc, one famous WIN32 API, has been implemented as one PALAPI (defined in Heap.c), to make it possible that the CLI/Rotor be ported smoothly to other OS, such freebsd/mac os.

            CRT routines are also reimplemented, such as memcpy, it has been implemented as GCSafeMemCpy

            There're many macros in fuctions, such as SCAN_IGNORE_FAULT/STATIC_CONTRACT_NOTHROW/STATIC_CONTRACT_NOTRIGGER, they are for static analysis tool to scan, analyse and figour out the potential issues in code.

            From view point of the execution model by CLI, the act of compiling (including JIT) high-level type descriptions would be separated from the act of turning these type descriptions into processor-specific code and memory structures.

            And such executino model, in other word, the well-known 'managed execution', would defer the loading, verification and compilation of components until runtime really needs; At the same time, the type-loading is the key trigger that causes CLI's tool chain to be engaged at runtime. Deferred compilation(lead to JIT)/linking/loading would get better portability to different target platform and be ready for version change; The whole deferred process would driven by well-defined metadata and policy, and it would be very robust for building a virtual execution environment;

            At the top of such CLI tool chain, fusion is reponsible for not only finding and binding related assemblies, which are via assembly reference defined in assembly, fusion also takes another important role, loader, and its part of functionality is implemented in PEAssembly, ClassLoader classes. For example, ClassLoader::LoadTypeHandleForTypeKey.

            For types in virtual execution environment of CLI, rotor defines four kinds of elements for internal conducting,
            ELEMENT_TYPE_CLASS for ordinary classes and generic instantiations(including value types);
            ELEMENT_TYPE_ARRAY AND ELEMENT_TYPE_SZARRAY for array types
            ELEMENT_TYPE_PRT and ELEMENT_TYPE_BYREF for pointer types
            ELEMENT_TYPE_FNPTR for function pointer types

            every type would be assigned unique ulong-typed token, and such token would be used to look up in m_TypeDefToMethodTableMap (Linear mapping from TypeDef token to MethodTable *)which is maintained by current module; If there it is, the pointer to method table of such type would be retrieved, or it would look up in the loader module, where the method table should exist in while it's JIT loaded, not launched from NGEN image;

            And all the unresolved typed would be maintained in a hash table, PendingTypeLoadTable; Types and only those types that are needed, such as dependencies, including parent types, are loaded in runtime, such type is fully loaded and ready for further execution, and other unresolved types would be kept in the previous hash table.

            posted @ 2010-12-13 09:02 flagman 閱讀(1643) | 評論 (0)編輯 收藏

            為何C++中的類成員函數沒有采用類似Java中的“全虛”設計

            關于程序設計語言本身的設計有許多有趣的話題,比如,為何C++中的類成員函數沒有采用類似Java中的“全虛”設計?

            1) 從語言本身設計上看,
            效率定然是c++當初設計時考慮的重點之一,舉個例子,為了節省不必要的VTable開銷,ATL用template技術靜態轉換來模擬動態綁定以支持COM特性的實現;和C的兼容,就VTable角度看,問題不大,因為后者可以用函數指針數組來模擬;

            2) 再從大多數應用中常見的類繼承體系上看,
            除了整個繼承體系所統一開放出來的接口集(也就是由虛函數所組成),在繼承體系的每個層面另外會有大量的其他輔助成員函數(其數量通常比虛函數多的多),這些成員函數完全沒必要設計成虛函數;

            3) 從其他語言看,
            即使較新的虛擬機語言C#(Java算是較老的虛擬機語言),反而定義了比C++更為嚴格更為顯式的成員方法實現或覆蓋或重載或新建的規則;這是非常重要的對C++以及Java設計思想的反思。

            4) 從語言的適用場合看,
            我們現在的討論,絕大多數情況下帶有一個非常重要的默認前提,那就是在用戶態模式下使用C++,如果放寬這個約束,在內核模式下使用C++,那情況又完全不同了。
            引用下面這個文檔的觀點,http://www.microsoft.com/china/whdc/driver/kernel/KMcode.mspx
            首先,用戶態下非常廉價幾乎不用考慮的資源,在內核中是非常昂貴的,比如內核堆棧一般就3個page;

            在內核不能分頁(paging)時必須保證將被執行的所有代碼和數據必須有效的駐留在物理內存中,如果這時需要多駐留幾張虛表以及虛表指針那還是顯得非常昂貴的,同時編譯器為虛函數,模板等生成代碼的方式,讓開發人員很難確定要執行一個函數所需要的所有代碼的所在位置,因此也無法直接控制用于安置這些代碼的節(個人認為可能通過progma segment/datasegment/codesegment對于代碼和數據進行集中控制),因此在需要這些代碼時,可能已經被page out了;

            所有涉及類層次結構,模板,異常等等這樣的一些語言結構在內核態中都可能是不安全的,最好是把類的使用限定為POD類,回到我們的主題虛函數,也就是說內核態下類設計中沒有虛函數。

            posted @ 2010-12-13 08:57 flagman 閱讀(1677) | 評論 (1)編輯 收藏

            僅列出標題  下一頁
            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            導航

            統計

            常用鏈接

            留言簿(1)

            隨筆分類

            隨筆檔案

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            精品无码人妻久久久久久| 亚洲国产精品成人AV无码久久综合影院 | 久久婷婷人人澡人人爽人人爱| www.久久99| 91久久精品视频| 久久只这里是精品66| 国内精品伊人久久久久777| 无码专区久久综合久中文字幕| 久久青青草原亚洲av无码app| 国产精品无码久久久久久| 国产99久久久久久免费看| 亚洲伊人久久综合影院| 一本色综合网久久| 久久久久黑人强伦姧人妻| 亚洲熟妇无码另类久久久| 久久久久综合中文字幕| 国内精品久久久久伊人av| 四虎影视久久久免费| 久久精品人人槡人妻人人玩AV| 久久久久人妻一区精品| 麻豆精品久久久久久久99蜜桃| 国产精品久久久99| 国内精品伊人久久久久AV影院| 国内精品久久久久久久亚洲| 久久经典免费视频| 久久av免费天堂小草播放| 久久精品人人槡人妻人人玩AV| 精品国产青草久久久久福利| 久久久久国产视频电影| 久久精品国产第一区二区三区| 99精品国产99久久久久久97| 色综合久久88色综合天天| 久久精品夜夜夜夜夜久久| 综合网日日天干夜夜久久| 久久人人爽人人爽人人片AV麻烦| 久久亚洲国产精品123区| 久久99国产精品久久99果冻传媒| 久久久久高潮毛片免费全部播放 | 人人狠狠综合久久亚洲| 国产成人香蕉久久久久| 草草久久久无码国产专区|