随笔-3  评论-5  文章-13  trackbacks-0

--------------------------------------------------------------------------------
标题: C++ 代码移植要点
作者: 叶飞虎
日期: 2010.09.06
--------------------------------------------------------------------------------

1. 分层设计
   隔离平台相关的代码, 就像可测试性一样, 可移植性也要从设计抓起。一般来说, 最上
   层和最下层都不具有良好的可移植性:
      1). 最上层是 GUI, 大多数 GUI 都不是跨平台的, 如: Win32 SDK 和 MFC
      2). 最下层是操作系统 API, 大部分操作系统 API 都是专用的

   如果这两层的代码散布在整个软件中, 那么这个软件的可植性将非常的差, 这是不言自
   明的。那么如何避免这种情况呢? 当然是分层设计了:
      1). 最底层采用 Adapter 模式, 把不同操作系统的 API 封装成一套统一的接口(如:
          KYLib 库), 至于封装成类还是封装成函数, 要看实际情况而定。如果在开发第
          一个平台时就采用 KYLib, 可以大大减少移植的工作量。

      2). 最上层采用分离界面表现与内部逻辑代码的模式, 把大部分代码放到内部逻辑里
          面, 界面仅仅是显示和接收输入, 即使要换一套 GUI, 工作量也不大。这同时也
          是提高可测试性的手段之一, 当然还有其它一些附加好处。所以即使你采用 QT
          或者 GTK+ 等跨平台的 GUI 设计软件界面, 分离界面表现与内部逻辑也是非常
          有用的。


2. 注意平台的特性
   a. 目录分隔符: 在Windows下用 '\\', 在Linux下用 '/'。

   b. 文本文件换行符: 在Windows下用 "\r\n", 在Linux下用 '\n'。

   c. 在 Windows 中文件名不区分大小写字母, 而在 Linux 中则区分大小写字母。

   d. 在 Windows 中线程可以 suspend 和 resume, 而在 Linux 中则不允许此操作。

   e. 在 Windows 的动态库中, 除非明确指明为 export 的函数外, 其它函数对外都是不
      可见的。

   f. 在 Linux 的共享库中, 所有非 static 的全局变量和函数, 对外全部是可见的。这
      要特别小心, 同名函数引起的问题, 让你查上两天也不为过。

   g. 在 Linux 的共享库中, 如果想绑定共享库里的全局符号(变量, 函数和类等等), 则
      在链接共享库的时候, 添加 gcc 选项 -Wl,-Bsymbolic 即可。

   h. 在 Linux 的共享库中, 如果共享库存取主程序里定义的全局符号, 链接主程序的时
      候, 使用参数 -Wl,--export-dynamic 即可。


3. 最好不要使用编译器特有的特性
   a. 像在 VC 里, 你要实现线程局部存储, 在变量前加一个 __declspec( thread ) 就行
      了, 然而尽管在 pthread 里有类似的功能, 却不能按这种方式实现, 所以无法移植
      到 Linux 下。

   b. 同样 gcc 也有很多扩展, 是在 VC 或者其它编译器里所没有的。如编译成多线程安
      全的选项 -pthread, 此选项在编译源程序和链接时使用。


4. 数据类型差别
   a. 在 VC 中64位整型是 __int64, 而在 Linux 中是 int64_t。

   b. 在 VC 中函数指针默认情况下可以直接赋值给 void* 类型变量, 而在 Linux 中则不
      允许直接赋值, 必须使用 (void*) 强制转换。

   c. 在 Windows 中的原子锁相关函数 InterlockXXX 中的参数类型是 long*,
      而在 Linux 中参数类型是 int*。


5. 调用外部库(静态库和动态库)差异
   a. 在 VC 中调用外部库有 .lib 支持, 若是动态库则直接通过 .lib 关联。

   b. 在 Linux 中调用静态库为 .a 文件, 库之间的先后顺序非常重要, 如 libKYLib.a
      和 libkylin.a, 且 kylin 依赖 KYLib, 则在工程中加载库的顺序必须为: 先加载
      libkylin.a, 再加载 libKYLib.a。

   c. 在 Linux 中调用动态库为 .so 文件, 如果有好几个库, 它们之间有一些依赖关系的
      话, 例如 X 依赖 Y, 那么你就要先加载那些被依赖的 Y, 然后加载 X。

   d. 在 Linux 中混合调用静态库和动态库, 如使用 libKYLib.a 和 libswgci32c.so, 且
      libswgci32c.so 中使用了 libKYLib.a, 则在加载库时必须先加载 libKYLib.a, 然
      后再加载 libswgci32c.so。


6. 加载动态库时查找路径顺序的差异
   a. Windows 库搜索路径和顺序
      1). 应用程序目录
      2). 当前工作目录
      3). 系统目录 (%systemroot%, %systemroot%\system 和 %systemroot%\system32),
          如: C:\WINNT\, C:\WINNT\system, C:\WINNT\system32
      4). 路径变量 (系统的环境变量 Path)

   b. Linux 库搜索路径和顺序
      1). 链接时指定的路径, 如: -Wl,-rpath=./ 选项表示编译时 ld 路径
      2). 环境变量 LD_LIBRARY_PATH 指明的路径
      3). /etc/ld.so.cache中的函数库列表
      4). /lib目录, 然后/usr/lib
      5). 当前工作目录


7. 动态库入口函数的差异
   a. Windows 中有 DllMain 入口函数, 而 Linux 中则没有。

   b. Linux 中有特殊函数 _init 和 _fini, 主要是分别用来初始化函数库和关闭的时候
      做一些必要的处理, 我们可以把自己认为需要的代码放到这两个函数里面, 它们分别
      在函数库被加载和释放的时候被执行。具体说, 如果一个函数库里面有一个名字为
      "_init" 的函数输出, 那么在第一次通过 dlopen() 函数打开这个函数库, 或者只是
      简单的作为共享函数库被打开的时候, _init 函数被自动调用执行。与之相对应的就
      是 _fini 函数, 当一个程序调用 dlclose() 去释放对这个函数库的引用的时候, 如
      果该函数库的被引用计数器为 0 了, 或者这个函数库是作为一般的共享函数库被使
      用而使用它的程序正常退出的时候, _fini就会被调用执行。

      C语言定义它们的原型如下:
      void _init(void);
      void _fini(void);

      当使用你自己的 _init 和 _fini 函数时, 会出现命名冲突, 就会得到一个
      "multiple-definition" 的错误, 编译器提示已经存在这个名字, 可以通过几种方式
      来解决:
         1). 自定义 init 函数名字, 比如 myinit 用 -Wl, 选项给 ld 传递此名字:
         gcc ... -Wl,-init=myinit

         2). 当 GCC 编译源程序时, 可以使用选项 -nostartfiles 来使共享库不与系统
             启动文件一起编译
         gcc ... -nostartfiles

         3). 使用上面的函数或 GCC 的 -nostartfiles 选项并不是很好的习惯, 因为这
             可能会产生一些意外的结果。相反, 库应该使用
             __attribute__((constructor)) 和 __attribute__((destructor)) 函数属
             性来输出它的构造函数和析构函数。如下所示:

             void __attribute__((constructor)) x_init(void);
             void __attribute__((destructor))  x_fini(void);

             构造函数会在dlopen()返回前或库被装载时调用;
             析构函数会在这样几种情况下被调用: dlclose() 返回前, 或 main() 返回
             后, 或装载库过程中 exit() 被调用时。

   c. Linux 中的初始化和释放函数不建议使用。

--------------------------------------------------------------------------------

posted on 2011-05-22 10:55 Kyee Ye 阅读(462) 评论(0)  编辑 收藏 引用 所属分类: C++类库KYLib

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理