• <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>
            Own Zone
            Lotto
            posts - 3,  comments - 3,  trackbacks - 0

            ps:這篇文章是Unix下對于c++以及c的dll及類的動態(tài)加載以及原理的說明,由此類推到windows平臺,也很實用

            Trackback: http://blog.chinaunix.net/u1/46723/showart_570402.html

            如何使用dlopen API動態(tài)地加載C++函數(shù)和類
            C++ dlopen mini HOWTO 中譯版 [原創(chuàng)]
             
            C++ dlopen mini HOWTO
            作者:Aaron Isotton <aaron@isotton.com> 2006-03-16
            譯者:Lolita@linuxsir.org 2006-08-05
            ------------------------------------------------
            摘要
              如何使用dlopen API動態(tài)地加載C++函數(shù)和類

            ------------------------------------------------
            目錄
              介紹
                版權(quán)和許可證
                不承諾
                貢獻者
                反饋
                術(shù)語
              問題所在
                Name Mangling
                類
              解決方案
                extern "C"
                加載函數(shù)
                加載類
              源代碼 
              FAQ
              其他
              參考書

            ------------------------------------------------
            介紹
              如何使用dlopen API動態(tài)地加載C++函數(shù)和類,是Unix C++程序員經(jīng)常碰到的問題。事實上,情況偶爾有些復(fù)雜,需要一些解釋。這正是寫這篇mini HOWTO的緣由。
              理解這篇文檔的前提是對C/C++語言中dlopen API有基本的了解。這篇HOWTO的維護鏈接是 http://www.isotton.com/howtos/C++-dlopen-mini-HOWTO/

              版權(quán)和許可證
              這篇文檔《C++ dlopen mini HOWTO》版權(quán)為Aaron Isotton所有(copyrighted (c) 2002-2006),任何人在遵守自由軟件基金會制定的GPLv2許可證條款前提下可以自由拷貝、分發(fā)和修改這份文檔。

              不承諾
              本文不對文中的任何內(nèi)容作可靠性承諾。您必須為您自己使用文中任何概念、示例和信息承擔風險,因為其中可能存在錯誤和不準確的地方,或許會損壞您的系統(tǒng)──盡管幾乎不可能發(fā)生此類事故,但您還是小心行事──作者不會為此負任何責任。

              貢獻者
              在這篇文檔中,我欣然致謝(按字母順序):
              ◆ Joy Y Goodreau <joyg (at) us.ibm.com> 她的編輯工作.
              ◆ D. Stimitis <stimitis (at) idcomm.com> 指出一些formatting和name mangling的問題, 還指出extern “C”的一些微妙之處。

              反饋
              歡迎對本文檔的反饋信息!請把您的補充、評論和批評發(fā)送到這個郵件地址:<aaron@isotton.com>。

              術(shù)語
              dlopen API
                關(guān)于dlclose、dlerror、dlopen和dlsym函數(shù)的描述可以在 dlopen(3) man手冊頁查到。
                請注意,我們使用“dlopen”時,指的是dlopen函數(shù),而使用“dlopen API”則是指整個API集合。
            ------------------------------------------------
            問題所在
              有時你想在運行時加載一個庫(并使用其中的函數(shù)),這在你為你的程序?qū)懸恍┎寮蚰K架構(gòu)的時候經(jīng)常發(fā)生。
              在C語言中,加載一個庫輕而易舉(調(diào)用dlopen、dlsym和dlclose就夠了),但對C++來說,情況稍微復(fù)雜。動態(tài)加載一個C++庫的困難一部分是因為C++的name mangling(譯者注:也有人把它翻譯為“名字毀壞”,我覺得還是不翻譯好),另一部分是因為dlopen API是用C語言實現(xiàn)的,因而沒有提供一個合適的方式來裝載類。
              在解釋如何裝載C++庫之前,最好再詳細了解一下name mangling。我推薦您了解一下它,即使您對它不感興趣。因為這有助于您理解問題是如何產(chǎn)生的,如何才能解決它們。

              Name Mangling
              在每個C++程序(或庫、目標文件)中,所有非靜態(tài)(non-static)函數(shù)在二進制文件中都是以“符號(symbol)”形式出現(xiàn)的。這些符號都是唯一的字符串,從而把各個函數(shù)在程序、庫、目標文件中區(qū)分開來。
              在C中,符號名正是函數(shù)名:strcpy函數(shù)的符號名就是“strcpy”,等等。這可能是因為兩個非靜態(tài)函數(shù)的名字一定各不相同的緣故。
              而C++允許重載(不同的函數(shù)有相同的名字但不同的參數(shù)),并且有很多C所沒有的特性──比如類、成員函數(shù)、異常說明──幾乎不可能直接用函數(shù)名作符號名。為了解決這個問題,C++采用了所謂的name mangling。它把函數(shù)名和一些信息(如參數(shù)數(shù)量和大小)雜糅在一起,改造成奇形怪狀,只有編譯器才懂的符號名。例如,被mangle后的foo可能看起來像foo@4%6^,或者,符號名里頭甚至不包括“foo”。
              其中一個問題是,C++標準(目前是[ISO14882])并沒有定義名字必須如何被mangle,所以每個編譯器都按自己的方式來進行name mangling。有些編譯器甚至在不同版本間更換mangling算法(尤其是g++ 2.x和3.x)。即使您搞清楚了您的編譯器到底怎么進行mangling的,從而可以用dlsym調(diào)用函數(shù)了,但可能僅僅限于您手頭的這個編譯器而已,而無法在下一版編譯器下工作。

              類
              使用dlopen API的另一個問題是,它只支持加載函數(shù)。但在C++中,您可能要用到庫中的一個類,而這需要創(chuàng)建該類的一個實例,這不容易做到。

            解決方案

              extern "C"
              C++有個特定的關(guān)鍵字用來聲明采用C binding的函數(shù):extern "C" 。 用 extern "C"聲明的函數(shù)將使用函數(shù)名作符號名,就像C函數(shù)一樣。因此,只有非成員函數(shù)才能被聲明為extern "C",并且不能被重載。盡管限制多多,extern "C"函數(shù)還是非常有用,因為它們可以象C函數(shù)一樣被dlopen動態(tài)加載。冠以extern "C"限定符后,并不意味著函數(shù)中無法使用C++代碼了,相反,它仍然是一個完全的C++函數(shù),可以使用任何C++特性和各種類型的參數(shù)。

              加載函數(shù)
              在C++中,函數(shù)用dlsym加載,就像C中一樣。不過,該函數(shù)要用extern "C"限定符聲明以防止其符號名被mangle。
              
              示例1.加載函數(shù)
            代碼:

            //----------
            //main.cpp:
            //----------
            #include <iostream>
            #include <dlfcn.h>

            int main() {
                using std::cout;
                using std::cerr;

                cout << "C++ dlopen demo\n\n";

                // open the library
                cout << "Opening hello.so...\n";
                void* handle = dlopen("./hello.so", RTLD_LAZY);
               
                if (!handle) {
                    cerr << "Cannot open library: " << dlerror() << '\n';
                    return 1;
                }
               
                // load the symbol
                cout << "Loading symbol hello...\n";
                typedef void (*hello_t)();

                // reset errors
                dlerror();
                hello_t hello = (hello_t) dlsym(handle, "hello");
                const char *dlsym_error = dlerror();
                if (dlsym_error) {
                    cerr << "Cannot load symbol 'hello': " << dlsym_error <<
                        '\n';
                    dlclose(handle);
                    return 1;
                }
               
                // use it to do the calculation
                cout << "Calling hello...\n";
                hello();
               
                // close the library
                cout << "Closing library...\n";
                dlclose(handle);
            }

            //----------
            // hello.cpp:
            //----------
            #include <iostream>

            extern "C" void hello() {
                std::cout << "hello" << '\n';
            }

              在hello.cpp中函數(shù)hello被定義為extern "C"。它在main.cpp中被dlsym調(diào)用。函數(shù)必須以extern "C"限定,否則我們無從知曉其符號名。
              警告:
              extern "C"的聲明形式有兩種:上面示例中使用的那種內(nèi)聯(lián)(inline)形式extern "C" , 還有才用花括號的extern "C" { ... }這種。 第一種內(nèi)聯(lián)形式聲明包含兩層意義:外部鏈接(extern linkage)和C語言鏈接(language linkage),而第二種僅影響語言鏈接。
              下面兩種聲明形式等價:
            代碼:

            extern "C" int foo;
            extern "C" void bar();


            代碼:

            extern "C" {
                extern int foo;
                extern void bar();
            }

              對于函數(shù)來說,extern和non-extern的函數(shù)聲明沒有區(qū)別,但對于變量就有不同了。如果您聲明變量,請牢記:
            代碼:

            extern "C" int foo;

            代碼:

            extern "C" {
                int foo;
            }

              是不同的物事(譯者注:簡言之,前者是個聲明; 而后者不僅是聲明,也可以是定義)。
              進一步的解釋請參考[ISO14882],7.5, 特別注意第7段; 或者參考[STR2000],9.2.4。在用extern的變量尋幽訪勝之前,請細讀“其他”一節(jié)中羅列的文檔。

              加載類
              加載類有點困難,因為我們需要類的一個實例,而不僅僅是一個函數(shù)指針。我們無法通過new來創(chuàng)建類的實例,因為類不是在可執(zhí)行文件中定義的,況且(有時候)我們連它的名字都不知道。
              解決方案是:利用多態(tài)性! 我們在可執(zhí)行文件中定義一個帶虛成員函數(shù)的接口基類,而在模塊中定義派生實現(xiàn)類。通常來說,接口類是抽象的(如果一個類含有虛函數(shù),那它就是抽象的)。
              因為動態(tài)加載類往往用于實現(xiàn)插件,這意味著必須提供一個清晰定義的接口──我們將定義一個接口類和派生實現(xiàn)類。
              接下來,在模塊中,我們會定義兩個附加的helper函數(shù),就是眾所周知的“類工廠函數(shù)(class factory functions)(譯者注:或稱對象工廠函數(shù))”。其中一個函數(shù)創(chuàng)建一個類實例,并返回其指針; 另一個函數(shù)則用以銷毀該指針。這兩個函數(shù)都以extern "C"來限定修飾。
              為了使用模塊中的類,我們用dlsym像示例1中加載hello函數(shù)那樣加載這兩個函數(shù),然后我們就可以隨心所欲地創(chuàng)建和銷毀實例了。

              示例2.加載類
              我們用一個一般性的多邊形類作為接口,而繼承它的三角形類(譯者注:正三角形類)作為實現(xiàn)。
            代碼:

            //----------
            //main.cpp:
            //----------
            #include "polygon.hpp"
            #include <iostream>
            #include <dlfcn.h>

            int main() {
                using std::cout;
                using std::cerr;

                // load the triangle library
                void* triangle = dlopen("./triangle.so", RTLD_LAZY);
                if (!triangle) {
                    cerr << "Cannot load library: " << dlerror() << '\n';
                    return 1;
                }

                // reset errors
                dlerror();
               
                // load the symbols
                create_t* create_triangle = (create_t*) dlsym(triangle, "create");
                const char* dlsym_error = dlerror();
                if (dlsym_error) {
                    cerr << "Cannot load symbol create: " << dlsym_error << '\n';
                    return 1;
                }
               
                destroy_t* destroy_triangle = (destroy_t*) dlsym(triangle, "destroy");
                dlsym_error = dlerror();
                if (dlsym_error) {
                    cerr << "Cannot load symbol destroy: " << dlsym_error << '\n';
                    return 1;
                }

                // create an instance of the class
                polygon* poly = create_triangle();

                // use the class
                poly->set_side_length(7);
                    cout << "The area is: " << poly->area() << '\n';

                // destroy the class
                destroy_triangle(poly);

                // unload the triangle library
                dlclose(triangle);
            }


            //----------
            //polygon.hpp:
            //----------
            #ifndef POLYGON_HPP
            #define POLYGON_HPP

            class polygon {
            protected:
                double side_length_;

            public:
                polygon()
                    : side_length_(0) {}

                virtual ~polygon() {}

                void set_side_length(double side_length) {
                    side_length_ = side_length;
                }

                virtual double area() const = 0;
            };

            // the types of the class factories
            typedef polygon* create_t();
            typedef void destroy_t(polygon*);

            #endif

            //----------
            //triangle.cpp:
            //----------
            #include "polygon.hpp"
            #include <cmath>

            class triangle : public polygon {
            public:
                virtual double area() const {
                    return side_length_ * side_length_ * sqrt(3) / 2;
                }
            };


            // the class factories
            extern "C" polygon* create() {
                return new triangle;
            }

            extern "C" void destroy(polygon* p) {
                delete p;
            }

              加載類時有一些值得注意的地方:
              ◆ 你必須(譯者注:在模塊或者說共享庫中)同時提供一個創(chuàng)造函數(shù)和一個銷毀函數(shù),且不能在執(zhí)行文件內(nèi)部使用delete來銷毀實例,只能把實例指針傳遞給模塊的銷毀函數(shù)處理。這是因為C++里頭,new操作符可以被重載;這容易導(dǎo)致new-delete的不匹配調(diào)用,造成莫名其妙的內(nèi)存泄漏和段錯誤。這在用不同的標準庫鏈接模塊和可執(zhí)行文件時也一樣。
              ◆ 接口類的析構(gòu)函數(shù)在任何情況下都必須是虛函數(shù)(virtual)。因為即使出錯的可能極小,近乎杞人憂天了,但仍舊不值得去冒險,反正額外的開銷微不足道。如果基類不需要析構(gòu)函數(shù),定義一個空的(但必須虛的)析構(gòu)函數(shù)吧,否則你遲早要遇到問題,我向您保證。你可以在comp.lang.c++ FAQ( http://www.parashift.com/c++-faq-lite/ )的第20節(jié)了解到更多關(guān)于該問題的信息。

            源代碼
              你可以下載所有包含在本文檔中的代碼包: http://www.isotton.com/howtos/C++-dl...xamples.tar.gz

            posted on 2008-06-06 23:52 Lotto 閱讀(3605) 評論(0)  編輯 收藏 引用

            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            Locations of visitors to this page

            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            常用鏈接

            留言簿(1)

            隨筆檔案

            文章檔案

            相冊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            国产精品日韩欧美久久综合| 午夜精品久久久久久久无码| 青青草原精品99久久精品66| 精品久久久噜噜噜久久久| 欧美久久综合性欧美| 久久国产精品二国产精品| 香蕉久久夜色精品国产尤物| 久久亚洲私人国产精品| 狠狠色伊人久久精品综合网| 亚洲va久久久久| 99久久精品国产一区二区| 婷婷国产天堂久久综合五月| 久久久久久久人妻无码中文字幕爆| 国产99久久九九精品无码| 日韩人妻无码一区二区三区久久99| 97精品国产91久久久久久| 合区精品久久久中文字幕一区| 国产麻豆精品久久一二三| 久久久久久久精品成人热色戒 | 国产一区二区三区久久| 亚洲精品美女久久久久99小说 | 亚洲国产天堂久久久久久 | 国产精品美女久久久久AV福利| 一本色道久久综合狠狠躁| 国产亚州精品女人久久久久久 | 国产精品免费久久| 久久久无码一区二区三区| 国产精品乱码久久久久久软件| 日本免费一区二区久久人人澡 | 久久ZYZ资源站无码中文动漫| 婷婷国产天堂久久综合五月| 久久精品国产一区二区三区| 婷婷久久综合九色综合98| 99国产精品久久| 99久久无色码中文字幕| 婷婷久久久亚洲欧洲日产国码AV | 久久青青草原精品国产软件| 久久er国产精品免费观看2| 国产精品99久久99久久久| 久久久久久无码Av成人影院| 伊人久久综合无码成人网|