澳门新蒲京娱乐

图片 4
可以取代

快速使用Word2007创建专业水准样式的内容,2007新增功能

对于C语言可移植性的思考,解决方案

带来的问题

  1. SomeFunction()
    函数代码冗余,格式混乱。本例仅涉及三个预编译选项,但实际情况中由于
    Linux
    版本众多并且可能涉及操作系统位数的问题,增加对新系统的支持会导致预编译选项不断增多,造成
    SomeFunction() 函数结构十分混乱。
  2. 新增其他平台相关接口(例如:增加
    SpecialCase3ForRHEL(),SpecialCase3ForSUSE(),SpecialCase3ForUBUNTU),会成倍增加代码中预编译宏的数量。
  3. 破坏了接口平台无关性的原则。SpecialCaseForRHEL(),SpecialCaseForSUSE(),SpecialCaseForUBUNTU()
    只是同一功能各个平台的不同实现,属于封装内容,不应该分开暴露给调用者。

可见,简单利用预编译宏来解决平台相关代码产生的问题不是一个好的方法,并没有解决本文开始提出的两个问题。后文将通过三个方案依次解决这些问题。

   
首先,在这里铺垫一下。学过Win32程序设计的人肯定都听说过APIApplication
Program
Interface)。我就先说说API,高手绕过。API对于程序员来说就是系统提供的接口,任何涉及系统调用都要通过API来完成。对于不同的操作系统都有不同的一套API,也就是说对于不同的操作系统系统调用的接口是完全不同的。所以在API层我们是不能移植的。

此方案的优点:

解决了预编译宏泛滥的问题,通过二进制分层可以将代码里的所有预编译选项去掉。遵循了接口平台无关性的原则。

可见,此方案很好地解决了本文开始提出的两个问题。

 
我相信学过C语言的同学,都会在书中看到C语言特点一定有:可移植性。但是什么是可移植?如何才能可移植?C语言是如何做到可移植的?对于初学者,可移植可能是一个经常遇到却很神秘的词。我想通过这篇文章来表达我对于可移植性的一些想法。

解决方案 3: 结合代理模式,桥接模式和单例模式进行优化

现在针对原始问题继续进行优化,摈弃方案 2
采用的分层手法,在单一库的范围内利用 C++ 多态特性和设计模式进行优化。

本文出自 “HelloWorld”
博客,请务必保留此出处

解决方案 2: 通过分层对进行优化

换一个角度来思考,可以在二进制层面对平台相关代码进行优化。通过对库的结构进行分层来优化,为每个
Linux 平台提供单独的实现库,并且把调用端独立提取出来。如下图所示:

      
下面我就来说说可移植,可移植顾名思义就是可以从一个平台移植到另外一个平台,但是大家一定要清楚,移植是基于操作系统的。但是这个时候,我们需要注意一点:基于各种操作系统平台不同,应用程序在二级制级别是不能直接移植的。我们只能在代码层去思考可移植问题,在API层面上由于各个操作系统的命名规范、系统调用等自身原因,在API层面上实现可移植也是不大可能的。那怎么才能实现可移植呢?
      
我们首先来看看现在主流的Windows和Linux平台下代码可移植性。有什么办法解决这个问题呢?答案是:在各个平台之间,基于大部分需求抽象出一个中间层。在中间层中,中间层用了屏蔽底层细节,在我们程序员看来C言语库就是这样一个中间层的作用。在各个平台下,我们默认C标准库中的函数都是一样的,这样基本可以实现可移植。但是对于C库本身而言,在各种操作系统平台下其内部实现是完全不同的,也就是说C库封装了操作系统API在其内部的实现细节。
      
因此,C语言提供了我们在代码级的可移植性,即这种可移植是通过C语言这个中间层来完成的。
      
当然,大家都可以看出上面的可移植是有条件的,C语言本身不能实现完全的可移植,为什么呢?因为,在我们程序中,我们经常会调用系统API,由于这些API在C语言中没有对其封装,所以我们只能用使用其原始的API,对于原始的API在各个操作系统中他们命名不同,就不能跨平台移植。所以,我们要写出完完全全的跨平台的程序,还是需要其他的一些手段。例如在我们的代码中下功夫。以下代码可以帮助我们实现各平台之间的可移植:
#ifndef _WINDOWS_        CreateThread();      //windows下线程的创建
#else        Pthread_create();    //Linux下线程的创建 #endif
对于头文件,也使用同样的预编译宏来实现。如: #ifndef _WINDOWS_       
#include <windows.h> #else        #include <thread.h>
#endif
这样就可以实现代码的可移植了。在编译的时候只要通过#define就可以选择在那个平台下完成程序的编译。
      
综上所述,我们都是将C,C++等各种语言当作中间层,以实现其一定程度上的可移植。如今,语言的跨平台的程序都是以这样的方式实现的。但是在不同的平台下,仍需要重新编译。  

清单 1. 设置预编译选项示例代码如下:
// Procedure.cpp 
 void SomeFunction() 
 { 
    //Common code for all linux 
    ...... 
    ...... 
 #ifdef RHEL 
    SpecialCaseForRHEL(); 
 #endif 

 #ifdef SUSE 
    SpecialCaseForSUSE(); 
 #endif 

 #ifdef UBUNTU 
    SpecialCaseForUBUNTU(); 
 #endif 

    //Common code for all linux 
    ...... 
    ...... 

 #ifdef RHEL 
    SpecialCase2ForRHEL(); 
 #endif 

 #ifdef SUSE 
    SpecialCase2ForSUSE(); 
 #endif 

 #ifdef UBUNTU 
    SpecialCase2ForUBUNTU(); 
 #endif 

    //Common code for all linux 
 ...... 
 ...... 
 }

开发人员可以通过设置 makefile 宏参数或者直接设置 gcc
参数来控制实际编译内容。

例如:

gcc -D RHEL Procedure.cpp -o Result.so -lstdc++   // Use RHEL marco

SpecialCaseForRHEL(),SpecialCaseForSUSE(),SpecialCaseForUBUNTU()
分别在该库 (Results.so) 的其他文件中予以实现。

Linux 平台相关代码带来的问题

目前市场上存在着许多不同的 Linux 平台(例如:RedHat, Ubuntu, Suse
等),各大厂商和社区都在针对自己支持的平台进行优化,为使用者带来诸多方便的同时也对软件研发人员在进行编码时带来不少问题:

  1. 由于程序中不可避免的存在平台相关代码(系统调用等),软件研发人员为了保证自己的产品在各个
    Linux
    平台上运行顺畅,一般都需要在源代码中大量使用预编译参数,这样会大大降低程序的可读性和可维护性。
  2. 接口平台无关性的原则是研发人员必须遵循的准则。但是在处理平台相关代码时如果处理不当,此原则很有可能被破坏,导致不良的编码风格,影响代码的扩展和维护。

本文将针对这两个问题循序渐进依次提出解决方案。

总结

本文开始提出平台相关代码造成的两个问题,接着循序渐进提出解决方案。在分析了常用的设置预编译选项方法的利弊的基础上,提出了一种新的利用
C++
多态特性,结合使用代理模式,桥接模式和单件模式处理平台相关代码的方案,并对这一方案予以扩展,给读者提供了一种新的高效的处理平台相关代码的方法。

本文首先提出平台相关代码造成的两个问题,然后针对这两个问题循序渐进依次提出解决方案,在分析了前两个方案弱点的基础上,最后着重介绍一种基于多种设计模式的
Linux 平台相关代码的解决方案,并给出此方案的 C++ 实现。

此方案的优点:

  1. 遵循接口平台无关性原则。此方案将各平台通用接口提升到最高的抽象层,易于理解和修改。
  2. 最大限度地降低预编译选项在源代码中的使用,实际上,本例中只需要在一处使用预编译宏,示例代码如下:

void Init() 
 { 
 #ifdef RHEL 
    RhelOS::init(); 
 #endif 

 #ifdef SUSE 
    SuseOS::init(); 
 #endif 

 #ifdef UBUNTU 
    UbuntuOS::init(); 
 #endif 
 }
  1. 源代码其他地方不需要添加预编译宏。
  2. 实现端和调用端都通过类的形式进行封装,而且实现端类和调用端类都可以自己单独扩展,完成一些各自需要完成的任务,所要保持一致的只是接口层函数。扩展性和封装性很好。

由此可见,此方案很好地解决了本文开始提出的两个问题,而且代码结构清晰,可维护型好。

接下来对上述源代码继续进行优化。上例 SuseHost/UbuntuHost/SUSEOS/UBUNTUOS
等类的实现被略去,实际上这些类的实现与 RhelHost 和 RHELOS
相似,可以利用宏来进一步优化框架代码结构。

目标效果:

  1. 源代码中尽可能减少预编译选项出现的频率,避免因功能扩展和平台支持的增加导致预编译宏数量爆炸。
  2. 完全遵循接口平台无关性的原则。
清单 2. 解决方案 1 示例代码如下:
// Procedure.cpp 
 void SomeFunction() 
 { 
    //Common code for all linux 
    ...... 
    ...... 
    SpecialCase(); 

    //Common code for all linux 
    ...... 
    ...... 

    SpecialCase2(); 

    //Common code for all linux 
    ...... 
    ...... 
 } 

 void SpecialCase() 
 { 
    //Common code for all linux 
    ...... 
    ...... 
 #ifdef RHEL 
    SpecialCaseForRHEL(); 
 #endif 

 #ifdef SUSE 
    SpecialCaseForSUSE(); 
 #endif 

 #ifdef UBUNTU 
    SpecialCaseForUBUNTU(); 
 #endif 

    //Common code for all linux 
    ...... 
    ...... 
 } 

 void Special2Case() 
 { 
    //Common code for all linux 
    ...... 
    ...... 
 #ifdef RHEL 
    SpecialCase2ForRHEL(); 
 #endif 

 #ifdef SUSE 
    SpecialCase2ForSUSE(); 
 #endif 

 #ifdef UBUNTU 
    SpecialCase2ForUBUNTU(); 
 #endif 

    //Common code for all linux 
    ...... 
    ...... 
 }

此方案的优点:

遵循了接口平台无关性原则,同样的功能只提供一个接口,每个平台的实现属于实现细节,封装在接口内部。此方案提供了一定的封装性,简化了调用者的操作。

图 2: 方案 2 的结构图

图片 1

此方案单独将调用端抽象出来,将每个平台实现端的相关代码提取出来做成一个单独的库(Rhel.so,Suse.so,Ubuntu.so)。SpecialCase()
为同一功能在不同平台的实现,采用相同接口名。底层库需要与 Results.so
库同时发布,也就是说,Redhat 版本发布时需同时打包 Results.so 和
Rhel.so,其他版本亦然。

相关文章

No Comments, Be The First!
近期评论
    功能
    网站地图xml地图