C_and_CPP 板


LINE

補充一些 PkmX 沒提到的東西和補一個簡單點的例子 ※ 引述《dreamboat66 (小嫩)》之銘言: : 假設我expose某函數void * GetInstance(int version); : 我可能會回傳兩種type, Type1 or Type2 : 使用者就要用 : auto inst = reinterpret_cast<Type1* or Type2>(GetInstance(version)); 而因為 C/C++ 無法在 runtime 知道 type 的細節 (reflection), 所以一般會約定好一致的介面 (API), 遵循一個 main program 已知的介面 來實作, 例如 class TypeCommon { public: virtual do_something(); virtual do_anotherthing(); }; class Type1: public TypeCommon { .. } class Type2: public TypeCommon { .. } TypeCommon *GetInstance(int version); 你這裡的盲點是, 主程式根本不需要知道 Type1, Type2, 想像一下 Firefox 外掛誰都可以寫, 而 Firefox 根本不需要知道那些外掛的存在 而主程式只要知道 TypeCommon 的樣子, dynamic load 來的 Type1, Type2 不過都是 當作 TypeCommon 在操作, 說穿了就只是基本的 interface/implemation 概念 : 之後就可以呼叫inst->Func1(); : 說到這邊我不了解的事情是 : 使用者並沒有.so or .lib : 我的這class Type1 在header裡面是不是要按照某一種規範來實作才能做到 : 不需要.so or .lib就能夠編譯自己的執行檔出來 基本上這是 linking 的事, 沒有指名道性要用到, linking 時就不需要 : class Type1{ : public: : 1. 是不是讓Type1整個class都只有pure virtual function即可 : virtual void Func() = 0; 如果你明白了主程式不需要知道 Type1 這件事, 其實 Type1 有沒有 pure 不重要. 新的問題是, 橋接 主程式和外掛的 TypeCommon 是不是要 pure? 答案是都可以, 但 link 時會有點差, 主要是 member 會被主程式和外掛都用到, 那應該由誰來提供的問題 : 2. 是不是有了非pure的virtual function, 編譯的時候就會需要.so or .lib來做link? : virtual void Func(); 不是. 會不會用到是看程式有沒有直接用到 Type1, Type2 : 3. 同上 : void Func(); ? : 4. 如果class內有member的話,是不是也要看這member的型態是不是也滿足 : 這邊要問的條件? : }; : 5. 還是說根本不是class 本身的問題而是要透過一些compiler關鍵字來做到? : dllexport or __attribute之類的? : 我自己因為只有微薄的windows開發經驗 印象中都需要提供.lib給使用者做link : 但又看到某些產品是可做到需要用到某功能的時候 : 才去server runtime download動態lib下來執行 : 這樣為什麼他在編譯自己執行檔時可以不需要.so or .lib一起做編譯呢? : 也不會遇到unresolved external symbol之類找不到定義的問題呢? : 謝謝 先舉一個不是 dynamic load 的例子, 然後我們再把他轉成 dlopen 的用法 // plugin.h 提供共同介面 #ifndef __PLUGIN_H #define __PLUGIN_H class plugin { public: virtual int getNum() = 0; int sum(); virtual ~plugin(); }; #endif // plugin.cc // 這個 plugin 很簡單, sum() 回傳 123 + 某個值, // 而每個實作這個 plugin 的人自行定義 getNum() #include "plugin.h" int plugin::sum() { return 123 + getNum(); } plugin::~plugin() {} // foo.cc // foo plugin 實作 getNum 為 111 #include "plugin.h" #include <iostream> class foo: public plugin { public: int getNum() override { return 111; } virtual ~foo() { std::cout << "foo deleted" << std::endl; } }; extern "C" plugin* new_foo() { // 提供一個 new foo 的方法 return new foo(); } // bar.cc // 同理你可以實作一個 bar, 實作不同的 getNum, 例如 222 // main.cc #include "plugin.h" #include <iostream> #include <dlfcn.h> extern "C" plugin* new_bar(); extern "C" plugin* new_foo(); int main () { // 從 main 的觀點, 不需要知道 foo 和 bar plugin *f = new_foo(); plugin *b = new_bar(); // 只要認得 plugin::getNum 和 plugin::sum 就好了 std::cout << f->getNum() << ", " << f->sum() << std::endl; // 111 234 std::cout << b->getNum() << ", " << b->sum() << std::endl; // 222 345 delete f; // foo delete delete b; // bar delete g++ -std=c++11 -pedantic \ main.cc plugin.cc foo.cc bar.cc -ldl - - - - - - - 以上就只是單的 C++ code, 應該大致可以理解? 如果是使用 dlopen 呢? 對主程式而言, 一般不會直接使用 new_foo, new_bar, 若每個 plugin 都有自已的 new function, 主程式還要先知道 new function 的名程, 所以可以定一個同名的 new function. 不同的 plugin (.so) 是不同的 link module, 不會有 multiple define 的問題. // in foo.cc/bar.cc extern "C" plugin* new_object() { // 提供一個 new foo 的方法 return new foo(); } 或是 foo.c 如果不限於在 dlopen 時動態載入, 也可以保留原本的 make_foo 再另外定一個 weak alias new_object 給 dlsym 時使用 extern "C" plugin* new_object () __attribute__((weak, alias("new_foo"))); // in main.cc // 用於 new_object 的 function pointer type extern "C" typedef plugin* (*new_fp)(); plugin *f, *b; // 分別開啟 libfoo, libbar 的 handle auto fh = dlopen("./libfoo.so", RTLD_LAZY); auto bh = dlopen("./libbar.so", RTLD_LAZY); // 固定使用 new_object 找出兩個 plugin 的 new function auto make_foo_fp = (new_fp) dlsym(fh, "new_object"); auto make_bar_fp = (new_fp) dlsym(fh, "new_object"); // 以下的用法其實就與原本大同小異了 f = make_foo_fp(); b = make_bar_fp(); std::cout << f->getNum() << ", " << f->sum() << std::endl; std::cout << b->getNum() << ", " << b->sum() << std::endl; delete f; delete b; - - - - # 若使用我上提供到的 weak alias 的做法, # foo/bar 可以直接與 main link 起來直接使用, # 也可以編成 shard object 透過 dlopen/dlsym 使用 CFLAGS="-std=c++11 -pedantic -g" g++ ${CFLAGS} -fpic -shared foo.cc plugin.cc -o libfoo.so g++ ${CFLAGS} -fpic -shared bar.cc plugin.cc -o libbar.so g++ ${CFLAGS} plugin.cc main.cc foo.cc bar.cc -ldl - - 這邊有另一個細節上面沒有提到. 因為 plugin class 有部份實作, 或本身的 type_info 這個實作應該由誰提供? 例如 foo class 本如果要乎叫 plugin::sum, 那這份 code 應該是主程式 a.out 還是 libfoo.so 提供? 以我上面的子, 其實 main, foo, bar 都會有一份 plugin class 的實作, 這些會有額外不必要的重覆. 而若 plugin class 本身 link 進 foo/bar, 會造成維護上的問題, 例如新版程式的 plugin class 改版. 為了避開這問題大至有兩種做法. 一) 改成由 main 主程式提供實作 CFLAGS="-std=c++11 -pedantic -g" g++ ${CFLAGS} -fpic -shared foo.cc -o libfoo.so g++ ${CFLAGS} -fpic -shared bar.cc -o libbar.so g++ ${CFLAGS} -rdynamnic plugin.cc main.cc foo.cc bar.cc -ldl 一般在 link 時, 若主式的 function 沒有被其他 shared object 使用到, 就不會 export 到 dynamic symbol 中, 若沒有被 export 到 dynamic table, 那這個 symbol 就不會被用來解析 dynamic loading. 例如 // foo.c void test(); void foo() { test(); } // main.c void test() {...} void foo(); int main () { foo (); } void bar() {... } $ gcc foo.c -fpic -shared -o libfoo.so $ gcc main.c -L. -lfoo 這就與 link static library (.a) 時的狀況一樣, 有可能 libfoo.so 本身不提供 test(), 而是其他 lib, 甚至 main 本身 提供 test() function. 差別只是 test() 會被 export 到 dynamic symbol table 供載入 libfoo.so 時使用. 但使用 dlopen 時, linker 並不會發生有人要使用 test() function. 所以 -rdynamic 在這的用途是告訴 linker, 有看不到的 user 會使用不知道哪個 sybmol, 把所有 symbol 都 export 出去. 不過這樣其實就太過頭了, 會有不必要的的 symbol 汙染. 而且大型專案 symbol 常常會數以萬計. 所以另一個做法其實就只是把 plugin 本身也變成 libray讓 main, foo,bar 供用 g++ ${CFLAGS} -fpic -shared plugin.cc -o libplugin.so g++ ${CFLAGS} -fpic -shared foo.cc -L. -lplugin -o libfoo.so g++ ${CFLAGS} -fpic -shared bar.cc -L. -lplugin -o libbar.so g++ ${CFLAGS} main.cc -L. -lplugin -ldl --



※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 114.32.204.230
※ 文章網址: https://webptt.com/m.aspx?n=bbs/C_and_CPP/M.1507399596.A.A13.html
1F:推 dreamboat66: 謝謝補充,需要花時間理解,但中間範例改用dlopen 10/08 02:37
2F:→ dreamboat66: 後 可以在主程式直接delete f and b嗎?不太確定觀 10/08 02:37
3F:→ dreamboat66: 念但印象是要提供release function 給主程式用 10/08 02:37
4F:→ PkmX: 如果你可以保證new_object回傳的pointer是new出來的 10/08 02:42
5F:→ PkmX: 而且主程式call的new/delete和library的完全符合的 是可以的 10/08 02:42
6F:→ PkmX: 保險起見library會自己提供release的函式給主程式使用 10/08 02:43
7F:→ PkmX: 因為只有library自己最清楚要如何解構他自己創造出來的物件 10/08 02:43
8F:推 dreamboat66: 是說他們編譯用的crt版本實作要一模一樣嗎? 10/08 02:45
9F:推 dreamboat66: 但我有印象曾經有提到 主程式跟lib 他們new出來的記 10/08 02:47
10F:→ dreamboat66: 憶體是配置在不同heap,所以你不能幫他delete會找不 10/08 02:47
11F:→ dreamboat66: 到之類的,是我記錯嗎還是有條件 10/08 02:47
12F:→ PkmX: 如果主程式/library 去重載 operator new/delete 就有可能 10/08 02:49
13F:→ PkmX: 不過這個還是回歸到兩邊的new/delete 不 compatible 的問題 10/08 02:49
14F:→ cole945: dreamboat66, 如你所提, 我這樣的寫法其實比較不好, 10/09 15:02
15F:→ cole945: API設計上應該是誰 allocate 出來的, 也要題供對應的 10/09 15:03
16F:→ cole945: deallocate, 或是應該要在 API 規範上講明應如何 delete 10/09 15:04
17F:→ cole945: 若沒有講明的話, 難保new_object會不會改變allocate的方 10/09 15:05
18F:→ cole945: 式. 例如new/malloc/或自帶heap pool. 10/09 15:05
19F:→ cole945: 這個例子主要是demo dlopen的部份, 所以就省delete_obj 10/09 15:06
20F:→ cole945: 省得太多code干擾主要的例子 :) 10/09 15:06
21F:→ cole945: 其實上面PkmX也幫忙解釋了..XD 10/09 15:07
22F:→ cole945: 正常來說libc 或 c++ runtime 不會自帶, 通常是dynamic 10/09 15:08
23F:→ cole945: link系統環境提供的, 所以 lib/main 的new/delete會相容 10/09 15:09
24F:→ cole945: 反過來說, 如果不是獨立的程式, 其實不建議 static link 10/09 15:09
25F:→ cole945: C/C++ runtime. 例如 staic link -ldl 會有warning 10/09 15:10
26F:推 dreamboat66: 所以exe跟dll會allicate在不同的heap這講法是錯的嗎 10/10 10:48







like.gif 您可能會有興趣的文章
icon.png[問題/行為] 貓晚上進房間會不會有憋尿問題
icon.pngRe: [閒聊] 選了錯誤的女孩成為魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一張
icon.png[心得] EMS高領長版毛衣.墨小樓MC1002
icon.png[分享] 丹龍隔熱紙GE55+33+22
icon.png[問題] 清洗洗衣機
icon.png[尋物] 窗台下的空間
icon.png[閒聊] 双極の女神1 木魔爵
icon.png[售車] 新竹 1997 march 1297cc 白色 四門
icon.png[討論] 能從照片感受到攝影者心情嗎
icon.png[狂賀] 賀賀賀賀 賀!島村卯月!總選舉NO.1
icon.png[難過] 羨慕白皮膚的女生
icon.png閱讀文章
icon.png[黑特]
icon.png[問題] SBK S1安裝於安全帽位置
icon.png[分享] 舊woo100絕版開箱!!
icon.pngRe: [無言] 關於小包衛生紙
icon.png[開箱] E5-2683V3 RX480Strix 快睿C1 簡單測試
icon.png[心得] 蒼の海賊龍 地獄 執行者16PT
icon.png[售車] 1999年Virage iO 1.8EXi
icon.png[心得] 挑戰33 LV10 獅子座pt solo
icon.png[閒聊] 手把手教你不被桶之新手主購教學
icon.png[分享] Civic Type R 量產版官方照無預警流出
icon.png[售車] Golf 4 2.0 銀色 自排
icon.png[出售] Graco提籃汽座(有底座)2000元誠可議
icon.png[問題] 請問補牙材質掉了還能再補嗎?(台中半年內
icon.png[問題] 44th 單曲 生寫竟然都給重複的啊啊!
icon.png[心得] 華南紅卡/icash 核卡
icon.png[問題] 拔牙矯正這樣正常嗎
icon.png[贈送] 老莫高業 初業 102年版
icon.png[情報] 三大行動支付 本季掀戰火
icon.png[寶寶] 博客來Amos水蠟筆5/1特價五折
icon.pngRe: [心得] 新鮮人一些面試分享
icon.png[心得] 蒼の海賊龍 地獄 麒麟25PT
icon.pngRe: [閒聊] (君の名は。雷慎入) 君名二創漫畫翻譯
icon.pngRe: [閒聊] OGN中場影片:失蹤人口局 (英文字幕)
icon.png[問題] 台灣大哥大4G訊號差
icon.png[出售] [全國]全新千尋侘草LED燈, 水草

請輸入看板名稱,例如:BabyMother站內搜尋

TOP