在编译时有效配置类层次结构

mbs*_*kel 11 c++ architecture embedded inheritance real-time

这个问题专门针对嵌入式硬实时系统上的C++架构.这意味着数据结构的大部分以及确切的程序流都是在编译时给出的,性能很重要,并且可以内联大量代码.解决方案最好只使用C++ 03,但也欢迎使用C++ 11输入.

我正在寻找建筑问题的既定设计模式和解决方案,其中相同的代码库应该被重复用于几个密切相关的产品,而某些部分(例如硬件抽象)必然是不同的.

我可能会最终得到一个封装在类中的模块的层次结构,这些模块可能看起来像这样,假设有4层:

Product A                       Product B

Toplevel_A                      Toplevel_B                  (different for A and B, but with common parts)
    Middle_generic                  Middle_generic          (same for A and B)
        Sub_generic                     Sub_generic         (same for A and B)
            Hardware_A                      Hardware_B      (different for A and B)
Run Code Online (Sandbox Code Playgroud)

这里,一些类继承自公共基类(例如,Toplevel_A来自Toplevel_base),而其他类则根本不需要专门化(例如Middle_generic).

目前我可以想到以下方法:

  • (A):如果这是一个常规的桌面应用程序,我将使用虚拟继承并在运行时创建实例,例如使用抽象工厂.

    缺点:然而,*_B类永远不会在产品A中使用,因此在运行时取消引用所有虚函数调用和未链接到地址的成员将导致相当大的开销.

  • (B)使用模板专门化作为继承机制(例如CRTP)

    template<class Derived>
    class Toplevel  { /* generic stuff ... */ };
    
    class Toplevel_A : public Toplevel<Toplevel_A> { /* specific stuff ... */ };
    
    Run Code Online (Sandbox Code Playgroud)

    缺点:难以理解.

  • (C):使用不同的匹配文件集,让构建脚本包含正确的文件

    // common/toplevel_base.h
    class Toplevel_base { /* ... */ };
    
    // product_A/toplevel.h
    class Toplevel : Toplevel_base { /* ... */ };
    
    // product_B/toplevel.h
    class Toplevel : Toplevel_base { /* ... */ };
    
    // build_script.A
    compiler -Icommon -Iproduct_A
    
    Run Code Online (Sandbox Code Playgroud)

    缺点:令人困惑,难以维护和测试.

  • (D):一个大的typedef(或#define)文件

    //typedef_A.h
    typedef Toplevel_A Toplevel_to_be_used;
    typedef Hardware_A Hardware_to_be_used;
    // etc.
    
    // sub_generic.h
    class sub_generic {
        Hardware_to_be_used the_hardware;
        // etc.
    };
    
    Run Code Online (Sandbox Code Playgroud)

    缺点:一个文件包含在任何地方,仍然需要另一种机制来实际切换不同的配置.

  • (E):类似的"基于策略"的配置,例如

    template <class Policy>
    class Toplevel { 
        Middle_generic<Policy> the_middle;
        // ...
    };
    
    // ...
    
    template <class Policy>
    class Sub_generic {
        class Policy::Hardware_to_be_used the_hardware;
        // ... 
    };
    
    // used as
    class Policy_A {
        typedef Hardware_A Hardware_to_be_used;
    };
    Toplevel<Policy_A> the_toplevel;
    
    Run Code Online (Sandbox Code Playgroud)

    缺点:现在一切都是模板; 每次都需要重新编译很多代码.

  • (F):编译器开关和预处理器

    // sub_generic.h
    class Sub_generic {
        #if PRODUCT_IS_A
            Hardware_A _hardware;
        #endif
        #if PRODUCT_IS_B
            Hardware_B _hardware;
        #endif
    };
    
    Run Code Online (Sandbox Code Playgroud)

    缺点:Brrr ......,只有在其他所有方法都失败的情况下.

是否存在任何(其他)已建立的设计模式或更好的解决方案,以便编译器可以静态分配尽可能多的对象并内联大部分代码,知道正在构建哪个产品以及哪些类将要进行使用?

Mat*_*son 7

我会去找A.直到它证明这不够好,去做与桌面相同的决定(当然,在堆栈上分配几千字节,或者使用多兆字节的全局变量可能是"明显"它不会起作用".是的,调用虚函数有一些开销,但是我会选择最明显和最自然的C++解决方案,然后重新设计它是否"足够好"(显然,尝试确定性能等等,并使用像一个抽样分析器,以确定你在哪里花时间,而不是"猜测" - 人类被证明是相当差的猜测者.

如果A被证明不起作用,我会转到选项B. 这确实不是很明显,但大致上,LLVM/Clang如何解决硬件和操作系统组合的问题,请参阅:https: //github.com/llvm-mirror/clang/blob/master/lib/Basic /Targets.cpp


Len*_*and 4

首先我想指出你基本上在问题中回答了你自己的问题:-)

接下来我想指出的是,在C++中

确切的程序流程在编译时给出,性能很重要,并且可以内联很多代码

称为模板。利用语言功能而不是构建系统功能的其他方法将仅作为在项目中构建代码的逻辑方式,以使开发人员受益。

此外,正如其他答案中所述,C对于硬实时系统比C++更常见,并且在C中,习惯上依赖MACROS在编译时进行这种优化。

最后,您在上面的B解决方案下注意到模板专业化很难理解。我认为这取决于您的操作方式以及您的团队在 C++/模板方面拥有多少经验。我发现许多“依赖模板”的项目非常难以阅读,而且它们产生的错误消息充其量也很糟糕,但我仍然设法在自己的项目中有效地使用模板,因为我在这样做时尊重 KISS 原则。

所以我对你的回答是,选择B或放弃C++而选择C