正文首先提出平台相关代码形成的多个难题,然后针对那四个难点规行矩步依次建议应用方案,在分条析理了前多少个方案劣点的底蕴上,最终关键介绍大器晚成种基于八种设计情势的
Linux 平台相关代码的建设方案,并交给此方案的 C++ 达成。

Linux 平台相关代码带给的难题

近期市情上存在重视重不及的 Linux 平台(举个例子:RedHat, Ubuntu, Suse
等),各大厂家和社区都在针对自身援救的平台张开优化,为使用者带来众多惠及的还要也对软件研究开发职员在拓宽编码时带来非常多主题素材:

  1. 鉴于程序中不可幸免的存在平台相关代码(系统调用等),软件研究开发人士以便保障自个儿的产品在乎气风发后生可畏Linux
    平台上运营通畅,日常都需求在源代码中一大波利用预编写翻译参数,那样会大大减少程序的可读性和可维护性。
  2. 接口平台毫无干系性的标准化是研究开发人士必得依照的法则。然而在拍卖平台相关代码时只要管理不当,此规范很有比异常的大可能被毁损,引致不良的编码风格,影响代码的扩展和保卫安全。

正文将本着那多个难题循途守辙依次提议建设方案。

透过安装预编译选项来管理平台相关代码

经过为各样平台安装相关的预编写翻译宏能够解决 Linux
平台相关代码的难题,实际意况下,比很多软件开荒职员也心服口服单独行使这种方法来减轻难点。

大器晚成经现成一动态库 Results.so,SomeFunction()是该库的三个导出函数,该库同一时间为 Rhel,Suse,Ubuntu 等八个阳台的 Linux
上层程序服务。(后文例子均依照此例并授予扩张。)

项目清单 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

SpecialCaseFor揽胜HEL(卡塔尔(قطر‎,SpecialCaseForSUSE(State of Qatar,SpecialCaseForUBUNTU()分别在该库 (Results.so卡塔尔 的别的文件中授予兑现。

图 1. 事项清单 1 代码的结构图

图片 1

带动的难题

  1. SomeFunction(卡塔尔(قطر‎函数代码冗余,格式混乱。本例仅提到多个预编写翻译选项,但实在乎况中出于
    Linux
    版本众多並且或许涉及操作系统位数的标题,扩张对新种类的支撑会形成预编写翻译选项不断追加,变成SomeFunction(卡塔尔 函数布局特别忙乱。
  2. 增加生产本事其余平台相关接口(比如:增加SpecialCase3ForTiggoHEL(卡塔尔国,SpecialCase3ForSUSE(State of Qatar,SpecialCase3ForUBUNTU),会倍增扩充代码中预编写翻译宏的数量。
  3. 毁掉了接口平台毫不相关性的口径。SpecialCaseForLacrosseHEL(卡塔尔(قطر‎,SpecialCaseForSUSE(State of Qatar,SpecialCaseForUBUNTU(卡塔尔国只是同风度翩翩作用各类平台的例外完成,归属封装内容,不该分别暴光给调用者。

看得出,轻松利用预编写翻译宏来消除平台相关代码发生的主题材料不是八个好的艺术,并未减轻本文开首提议的四个难题。后文将透过多个方案依次解除这个主题素材。

解决方案 1:依照接口平台毫无干系性原则开展优化

实为上,SpecialCaseFor福睿斯HEL(卡塔尔,SpecialCaseForSUSE(卡塔尔,SpecialCaseForUBUNTU(卡塔尔国只是同风流罗曼蒂克效能在区别平台上的兑现,SpecialCase2For奥迪Q5HEL(State of Qatar,SpecialCase2ForSUSE(State of Qatar,SpecialCase2ForUBUNTU(卡塔尔亦如此。对于调用者,应该根据接口平台非亲非故性的标准化,使用统一的接口进行调用,那样技术简化代码,使代码易于维护。

清单 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: 通过分支对拓宽优化

换二个角度来思索,能够在二进制层面前蒙受平台相关代码进行优化。通过对库的布局举办分层来优化,为各样Linux 平台提供单身的落到实处库,而且把调用端独立提抽出来。如下图所示:

图 2: 方案 2 的布局图

图片 2

此方案单独将调用端抽象出来,将各样平台达成端的相关代码提抽取来做成三个单身的库(Rhel.so,Suse.so,Ubuntu.so)。SpecialCase(卡塔尔国为同后生可畏作用在不相同平台的兑现,采纳同一接口名。底层库供付与 Results.so
库同有的时候候宣布,约等于说,Redhat 版本公布时需同期包装 Results.so 和
Rhel.so,其余版本相似。

此方案的帮助和益处:

缓慢解决了预编写翻译宏泛滥的难点,通过二进制分层能够将代码里的有着预编写翻译选项去掉。固守了接口平台毫不相关性的基准。

看得出,此方案很好地解决了本文起首建议的七个难点。

此方案的劣势:

老是发布 Results.so
的时候,底层库要求伴随一齐发表,诱致可进行李包裹文件数量成倍扩充。并且大多小程序,小工具的揭橥往往接纳单独的二进制文件,不容许有底层库的存在。

建设方案 3: 结合代理格局,桥接情势和单例方式张开优化

近年来本着原始难点持续张开优化,甩掉方案 2
接收的支行手法,在单纯库的范围Nelly用 C++ 多态性情和设计情势进行优化。

对象意义:

  1. 源代码中尽只怕收缩预编写翻译选项现身的频率,制止因效果与利益扩充和平台接济的加码以致预编写翻译宏数量爆炸。
  2. 一同依照接口平台毫无干系性的法规。
项目清单 3. 解决方案 3 调用端示例代码如下:
// Procedure.cpp 
 void SomeFunction() 
 { 
    //Common code for all linux 
    ...... 
    ...... 
    XXHost::instance()->SpecialCase1(); 

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

 XXHost::instance()->SpecialCase2(); 

    //Common code for all linux 
    ...... 
 ...... 
 }
图 3:方案 3 的具体得以达成类图

图片 3

此方案组合校订的代理方式(Proxy),桥接形式(Bridge)和单件方式(Singleton),并行使
C++ 封装、世襲和多态特性予以兑现。

IHost
是顶层抽象接口类,表明了达成端须要完毕的法力函数以至调用端供给调用的接口函数。

图 3 右半部分派生自 IHost 的大器晚成大器晚成类为达成端,在落实端,

为每一个 Linux
系统独立实现了二个类,互相之间非亲非故联性。该类实现了该操作系统平台相关的功效(SpecialCase1()和 SpecialCase2(卡塔尔国),即实现了平台相关代码。各种完毕类使用单件情势。

Init(卡塔尔 和 terminate(卡塔尔国 用来初叶化和清理操作。Init(State of Qatar函数首先成立和谐(单件方式),其次创造侧面代理类(单件形式,见下段描述),最终将和煦的内部存款和储蓄器地址通过
SetHost(卡塔尔 函数交给侧边代理方。

图 3 左半片段派生自 Host 的生机勃勃风姿罗曼蒂克类为调用端,在调用端,

Host 类做了风姿浪漫层封装,RhelHost 等派生类为实在的代理者(调用者),每一个Host 的派生类分别代表风流浪漫种必要(调用方),是左边手完成类的三个代理,举例RhelHost 是 RhelOS 的代理,SuseHost 是 SuseOS 的代办,UbuntuHost 是
UbuntuOS 的代办。每一种 Host 的派生类采用单件格局。

Host 类和 HostImp 类之间利用桥接的设计格局,利用 C++ 多态天性,最后通过
HostImp 类调用完毕端类的落到实处。调用端的调用进度如下:

  1. 因而 RhelHost 的指针调用 SpecialCase(State of Qatar,由于 RhelHost::SpecialCase(卡塔尔未有隐瞒基类虚函数的落到实处,实际调用的是 Host::SpecialCase(State of Qatar。
  2. Host 的有着调用被桥接到 HostImp 对应的函数。
  3. 由 HostImp 类调用明确的达成端的某八个目的的呼应达成函数(HostImp
    类的 SetHost(State of Qatar 函数记录了左侧类的靶子内部存款和储蓄器地址)。

项目清单 4. 化解方案 3 框架首要源代码如下:

// Host.h 
 class IHost 
 { 
 public: 
    virtual void SpecialCase1() = 0; 
    virtual void SpecialCase2() = 0; 
 }; 

 class Host : public IHost 
 { 
 public: 
    virtual ~Host() {}; 
    void setHost(IHost* pHost) 
    { 
        m_pImp->setHost(pHost); 
    } 
    virtual void SpecialCase1() 
    { 
        m_pImp->SpecialCase1(); 
    }; 
    virtual void SpecialCase2() 
    { 
        m_pImp->SpecialCase2(); 
    }; 

 protected: 
    Host(HostImp * pImp); 

 private: 
    HostImp* m_pImp; 
    friend class HostImp; 
 }; 

 class RhelHost : public Host 
 { 
 public: 
    static RhelHost* instance(); 
 private: 
    RhelHost(HostImp* pImp); 
 }; 

 RhelHost * RhelHost::instance() 
 { 
    static RhelHost * pThis = new RhelHost (new HostImp()); 
    return pThis; 
 } 

 RhelHost:: RhelHost (HostImp* pImp) 
 : Host(pImp) 
 { 
 } 

 class RhelOS : public IHost 
 { 
 public: 
    static void init() 
    { 
        static RhelOS me; 
        RhelHost::instance()->setHost(&me); 
    } 
    static void term() 
    { 
        RhelHost::instance()->setHost(NULL); 
    } 
 private: 
    virtual void SpecialCase1() 
    { 
        /* Real Operation */ 
    }; 
    virtual void SpecialCase2() 
    { 
        /* Real Operation */ 
    }; 

 }; 

 // HostImp.h 
 class HostImp : public IHost 
 { 
 private: 
    HostImp(const HostImp&); 

 public: 
    HostImp(); 
    virtual ~HostImp() {}; 
    void setHost(IHost* pHost) 
    { 
        m_pHost = pHost; 
    }; 
    virtual void SpecialCase1() 
    { 
        if(m_pHost != NULL) 
            m_pHost->SpecialCase1() 
    } 
    virtual void SpecialCase2() 
    { 
        if(m_pHost != NULL) 
            m_pHost->SpecialCase2() 
    } 

 private: 
    IHost* m_pHost; 
 };

此方案的帮助和益处:

  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 和 LacrosseHELOS
相像,能够应用宏来进一层优化框架代码布局。

清单 5. 消除方案 3 框架主要源代码优化:
#define HOST_DECLARE(name)  
 class ##nameHost : public Host  
 {  
 public:  
    static ##nameHost* instance();  
 private:  
    ##nameHost(HostImp* pImp);  
 }; 

 #define HOST_DEFINE(name)  
 ##nameHost* ##nameHost::instance()  
 {  
    static ##nameHost* pThis = new ##nameHost(new HostImp());  
    return pThis;  
 }  
 ##nameHost::##nameHost(HostImp* pImp)  
 : Host(pImp)  
 {  
 } 

 #define HOST_IMPLEMENTATION(name)  
 class ##name##OS : public IHost  
 {  
 public:  
    static void init()  
    {  
        static ##name##OS me;  
        ##nameHost::instance()->setHost(&me);  
    }  
    static void term()  
    {  
        ##nameHost::instance()->setHost(NULL);  
    }  
 private:  
 virtual void SpecialCase1();  
    virtual void SpecialCase2();  
 };

采纳八个宏来管理常常代码。至此,优化实现。从源代码角度来深入分析,作为完结端的开荒职员,只须求三步就足以成功操作:

  1. 调用 init() 函数;
  2. 使用 #define HOST_IMPLEMENTATION(name);    例如:#define
    HOST_IMPLEMENTATION(DEBIAN)
  3. 贯彻 DEBIAN::SpecialCase1(State of Qatar 和 DEBIAN::SpecialCase2(卡塔尔国。   
    作为调用端的开采职员,也只供给三步就足以做到操作:
  4. 使用 #define HOST_DECLARE(name卡塔尔(قطر‎ 进行宣示;    举例 : #define
    HOST_DECLARE(DEBIAN)
  5. 使用 #define HOST_DEFINE(name卡塔尔 举行定义;    举例: #define
    HOST_DEFINE (DEBIAN)
  6. 调用接口。    比如:
    DEBIANHost::instance(State of Qatar->SpecialCase1(State of Qatar;DEBIANHost::instance(卡塔尔->SpecialCase2(卡塔尔(قطر‎;

可知,优化后方案的代码清晰,不失为一个精美的阳台相关代码的减轻方案。

出于调用端和贯彻端往往要求传递参数,能够通过 特略Case1(卡塔尔(قطر‎函数的参数来传递参数,雷同的那个参数类能够因而桥接的办法予以兑现,本文不再详述,读者能够团结尝试。

对方案 3 的扩展

增加 1:对纯粹操作系统多对多的强大

对于方案 3 的落实,也可以有读者会问,调用端只需求 Host
类无需其派生类就可以到位方案 3 中的效用,的确如此,因为方案 3
中的代理类一贯是生机勃勃对后生可畏的关联,即 RhelHost 代理 RhelOS,Redhat
下只存在这里生机勃勃对风姿罗曼蒂克的涉嫌。不过其真实情境况下,单生龙活虎系统下异常的大概存在多对多的涉嫌。

例如,在单纯操作系统中,可能要求同期完毕三种作风的窗口。实际上,产生了多对多的代办关系。

图 4:单风度翩翩操作系统不一致 c 风格窗口的贯彻类图

图片 4

Style1Host 代理 Style1Dialog,Style2Host 代理 Style2Dialog,Style3Host
代理
Style3Dialog,八个窗口同期现存,也正是说左边八个实现类的实例和左边多少个代理类的实例相同的时间存在。可以预知,方案
3
的设计方式扩充性出色,达成端和调用端都能够在依照接口不改变性的事态下独自扩充本身的法力。

恢宏 2:对多操作系统的恢宏

方案 3 不只好够本着 Linux
平台相关代码进行拍卖,仍可以增加到任何许多场馆。举例,以往的程序库往往供给针对四个操作系统,包蕴Windows, Linux, Mac。每一个操作系统往往选拔差别的 GUI
库,那样在落到实处窗口操作的时候一定涉及到阳台相关代码。相近可以用方案 3
予以兑现。

图 5:多操作系统的兑现类图

图片 5

总结

本文开头提议平台相关代码产生的多个难点,接着安分守纪提议技术方案。在剖判了常用的设置预编写翻译选项措施的得失的功底上,提出了大器晚成种新的接收C++
多态本性,结合使用代理格局,桥接格局和单件格局管理平台相关代码的方案,并对这一方案予以扩张,给读者提供了风度翩翩种新的高速的管理平台相关代码的主意。

相关文章