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/cn.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灯, 水草

请输入看板名称,例如:iOS站内搜寻

TOP