楔子:
本文主要描述了把第三方代码移植到某一目标平台过程中所发生的typedef重复定义的问题。之所以要写这个问题,原因有三:1>这个问题是一个移植代码过程中经常遇到的老大难问题2>我还没有完全(或者说完美的)解决这个问题3>想得到诸位高手的点拨。
首先要看看下面的原始代码(为了突出重点,我简化了代码结构。实际代码要复杂的多):
a.c和a.h代表主开发平台的代码,下面简称为A平台代码;b.c和b.h代表要移植到A平台的第三方的代码,下面简称为B代码。
<代码片断I>
现在进入正题。我们想在a.c中使用B代码中的b_bar()这个函数,常规的方法是这样的:
<代码片断II>
那么我们应该如何搞定?有大概三种方法来解决这个问题,我们一一来看。
一、extern声明法
这种方法的基本思想是讲问题由编译阶段推迟到链接阶段。我一般会优先使用这种方法,代码如下:
<代码片断III>
方法一的缺点:1>修改extern的B代码的声明中的类型为基本类型,看起来代码不是很优雅;2>如果A代码中多处使用B代码,或者要使用B代码中的多个函数,大量的extern将使代码看起来非常的恶心。
二、统一typedef法
我所使用的X公司的源代码实际上也是有好几个部分拼接而成的,每个部分都有自己的typedef。它的代码中通过如下方法来规避typedef重复定义的问题:
<代码片断IV>
这样一来,B代码在b.h进行typedef的时候,如果检测到目前代码中已经存在了INT32这个定义,就不在进行这个定义了,就使用现存的INT32这个定义。这样一来,其实B代码在使用short来替代INT32,而不是它的原始期望中的long。我个人认为,这会导致B代码中的某些实现不稳定、出错。所以在移植第三方代码过程中,我绝对不推荐这种用法。(这是开讨论会中争议最大的地方,如果你有金砖,千万不要吝惜,拍过来吧~~让暴风雨来的更猛烈些吧)
三、类型适配法
这段文字已经不是再解决上面的问题了,纯粹是做为一种引申的思路而已。某些公司的代码会提供自己名字前缀的typedef,如ABC公司的类型定义就是ABC_INT32,而XXX公司的类型定义就是XXX_INT32。这样就能保证自己的类型定义总是在自己内部使用,而不至于引起冲突来。
<代码片断V>
修a.h 使用代码片断1的,a.c使用代码片断2的。
这样就不会在A代码和B代码之间引起类型定义冲突了。如果类型定义复杂了,有可能在A和B之间要进行强制类型转换,这点的确不爽。不过一般B代码会引入一个类型适配层,用typedef来把A和B之间的类型建立等价关系,这样就一点问题也没有了。
在A代码中使用B代码的时候就include这个b_type_adapt.h,这样A就能正确识别出B的类型来。问题就完全解决了。
总结:遗憾的是,有很多公司的代码都不是按照方法3来做的,它们都认为自己比较牛,自己应该定义原始类型,%^&(×)#%¥^&…… 唉,就苦了我们porting engineer了。因为是做porting,所以我不能把B中的typedef的类型做个全文替换,这样以后升级B代码就更头疼了。唉,这年头混口饭吃不容易阿~~
-------------
本文主要描述了把第三方代码移植到某一目标平台过程中所发生的typedef重复定义的问题。之所以要写这个问题,原因有三:1>这个问题是一个移植代码过程中经常遇到的老大难问题2>我还没有完全(或者说完美的)解决这个问题3>想得到诸位高手的点拨。
首先要看看下面的原始代码(为了突出重点,我简化了代码结构。实际代码要复杂的多):
a.c和a.h代表主开发平台的代码,下面简称为A平台代码;b.c和b.h代表要移植到A平台的第三方的代码,下面简称为B代码。
<代码片断I>
//filename: b.h #ifndef B_H #define B_H typedef long INT32; void b_bar(INT32 num); #endif //B_H
//filename: b.c #include <stdio.h> #include "b.h" void b_bar(INT32 num) { printf("b_bar trace: %d\n", num); }
//filename: a.h #ifndef A_H #define A_H typedef short INT32; void a_foo(INT32 num); #endif //A_H
//filename: a.c #include <stdio.h> #include "a.h" void a_foo(INT32 num) { printf("a_foo trace: %d\n", num); } int main() { INT32 abc=100; a_foo(abc); }源代码中A代码和B代码中并无任何瓜葛。A代码和B代码中都定义了一个叫做INT32的类型,但是在A代码中INT32是以short为原型的,就是说A代码中所有使用INT32的类型都是期望使用short的。同理,B代码中的所有使用INT32类型的地方都是期望使用long的。因此,我们只要在移植过程中保证A代码和B代码中的类型定义都满足自己原始的期望,那么这个移植过程中类型定义就是成功的,代码运行起来也就会很稳定;反之,移植的代码就存在着极大的风险。
现在进入正题。我们想在a.c中使用B代码中的b_bar()这个函数,常规的方法是这样的:
<代码片断II>
//filename: a.c #include <stdio.h> #include "a.h" Add this include sentence,We want use B 's declare #include "b.h" void a_foo(INT32 num) { printf("a_foo trace: %d\n", num); } void a_xxx(INT32 num) / Add this function { printf("a_xxx trace: %d\n", num); b_bar(num); / Call b_bar() function } int main() { INT32 abc=100; a_foo(abc); a_xxx(abc); / Use a_xxx() function }但是,我们代码如果这么使用的话,就会出错。错误的原因大概是:redefinition; different basic types。重复定义,不一致的基本类型。因为A代码和B代码中都定义了INT32这个类型。
那么我们应该如何搞定?有大概三种方法来解决这个问题,我们一一来看。
一、extern声明法
这种方法的基本思想是讲问题由编译阶段推迟到链接阶段。我一般会优先使用这种方法,代码如下:
<代码片断III>
//filename: a.c #include <stdio.h> #include "a.h" ///Use extern declare instead of include sentence, and use long instead of INT32 extern void b_bar(long num); void a_foo(INT32 num) { printf("a_foo trace: %d\n", num); } void a_xxx(INT32 num) / Add this function { printf("a_xxx trace: %d\n", num); b_bar(num); / Call b_bar() function } int main() { INT32 abc=100; a_foo(abc); a_xxx(abc); / Use a_xxx() function }如上面代码所示,我不再在A代码里面include B代码的.h文件,这样就避免了typedef的重复定义。同时,我修改了extern的b_bar()的声明,使其使用C语言基本类型long,这样就满足了B代码的原始期望,B代码运行起来就不会有错。通过extern,将问题推迟到链接期,链接的过程中是以原始类型为基准的,所以链接起来也不会出错。基本上就解决了这个问题。
方法一的缺点:1>修改extern的B代码的声明中的类型为基本类型,看起来代码不是很优雅;2>如果A代码中多处使用B代码,或者要使用B代码中的多个函数,大量的extern将使代码看起来非常的恶心。
二、统一typedef法
我所使用的X公司的源代码实际上也是有好几个部分拼接而成的,每个部分都有自己的typedef。它的代码中通过如下方法来规避typedef重复定义的问题:
<代码片断IV>
//filename: b.h #ifndef B_H #define B_H #ifndef A_H typedef long INT32; #endif void b_bar(INT32 num); #endif //B_H修改b.h,而b.c、a.h 使用代码片断1的,a.c使用代码片断2的。
这样一来,B代码在b.h进行typedef的时候,如果检测到目前代码中已经存在了INT32这个定义,就不在进行这个定义了,就使用现存的INT32这个定义。这样一来,其实B代码在使用short来替代INT32,而不是它的原始期望中的long。我个人认为,这会导致B代码中的某些实现不稳定、出错。所以在移植第三方代码过程中,我绝对不推荐这种用法。(这是开讨论会中争议最大的地方,如果你有金砖,千万不要吝惜,拍过来吧~~让暴风雨来的更猛烈些吧)
三、类型适配法
这段文字已经不是再解决上面的问题了,纯粹是做为一种引申的思路而已。某些公司的代码会提供自己名字前缀的typedef,如ABC公司的类型定义就是ABC_INT32,而XXX公司的类型定义就是XXX_INT32。这样就能保证自己的类型定义总是在自己内部使用,而不至于引起冲突来。
<代码片断V>
//filename: b.h #ifndef B_H #define B_H typedef long B_INT32; void b_bar(B_INT32 num); #endif //B_H
//filename: b.c #include <stdio.h> #include "b.h" void b_bar(B_INT32 num) { printf("b_bar trace: %d\n", num); }
修a.h 使用代码片断1的,a.c使用代码片断2的。
这样就不会在A代码和B代码之间引起类型定义冲突了。如果类型定义复杂了,有可能在A和B之间要进行强制类型转换,这点的确不爽。不过一般B代码会引入一个类型适配层,用typedef来把A和B之间的类型建立等价关系,这样就一点问题也没有了。
//filename: b_type_adapt.h typedef B_INT32 A_INT; typedef B_UINT32 A_UINT;
在A代码中使用B代码的时候就include这个b_type_adapt.h,这样A就能正确识别出B的类型来。问题就完全解决了。
总结:遗憾的是,有很多公司的代码都不是按照方法3来做的,它们都认为自己比较牛,自己应该定义原始类型,%^&(×)#%¥^&…… 唉,就苦了我们porting engineer了。因为是做porting,所以我不能把B中的typedef的类型做个全文替换,这样以后升级B代码就更头疼了。唉,这年头混口饭吃不容易阿~~
-------------