2017-03-06 38 views
0

有什么办法可以在最终的二进制文件中合并模板类的相同成员函数吗?我有可能支持特定的功能,并依赖于一个类,可能需要一些额外的步骤里面的成员函数:根据实际g ++:合并模板成员函数实例

Driver<Feature::A>::setDriver<Feature::A | Feature::B>::set
template <uint32_t features> 
class Driver { 
    static bool set (uint32_t value) { 
     /* do something required for every feature */ 
     if (features & Feature::A) 
     /* do special things if Feature::A */ 
     /* do something required for every feature */ 
     if (features & Feature::B || features & Feature::C) 
     /* do something special for either Feature::B or Feature::C */ 
     return true; 
    } 
    /* more, similar methods */ 
}; 

在代码后,我会再使用电话可用功能。到目前为止,所有这些工作都很顺利,只有使用g++-5.4.0 -std=c++11 -O3编译的已发布代码才会合并完全相同的方法,所以我希望使用它。在这个类中具有多个功能,其中主要的共同部分,真正打破了最终的二进制文件的大小。在使用私有函数用于公用部分减小了尺寸的开销,但不是友好的在我看来阅读:

template <uint32_t features> 
class Driver { 
    static void _set_common_0 (value) { 
     /* do something required for every feature */ 
    } 
    static bool set (uin32_t value) { 
     _set_common_0 (value); 
     if (features & Feature::A) 
      _set_special_0 (value); 
     _set_common_1 (value); 
     if (features & Feature::B || features & Feature::C) 
      _set_special_1 (value); 
     return true; 
    } 
}; 

而且这引入了问题,如果是在特殊的一个一个“早出”路径部分,以前可以通过一个简单的return现在可以完成从子功能升级,根据这些子功能的返回值进行进一步检查...

我想要的是编译器为Driver<Feature::B>::setDriver<Feature::C>::set发出(使用时)符号,但让它们通过代码指向相同的代码位置,因为它们是相同的。任何想法我怎么能做到这一点? (优选地,用C++ 11保持,且不需要从一个较新的标准功能)

编辑:为了澄清,我想知道,为什么相同的指令序列(由模板实例此处产生)不被gcc组合。据我的理解,-ftree-tail-merge应该至少替换那些跳转到相同的实现/代码序列(例如Driver<Feature::B>::setDriver<Feature::C>::set)。

+1

您真的需要一个类模板吗?因为这不是应该如何使用模板:你正在运行时检查编译时间常量:'if(features&Feature :: A)' –

+0

它不是一个真正的编译时间常量。应该使用的功能是运行时依赖和编译过程中不知道的。在运行时环境的Dependend上,我可以使用'Feature :: A'或不能使用它。 '驱动程序 :: set'调用不是直接发出,而是通过在初始化期间分配的函数指针,依赖于检测到的环境特征。 – Jonas

回答

0

从您发布的代码中可以清楚地看到您滥用模板。正确的方法是定义的Driver部分特例:

template <uint32_t features> 
class Driver; // There may be some default implementation, but it is not necessary 

// Specialize for feature A only 
template <> 
class Driver<Feature::A> { 
    bool set (uint32_t value) { 
     /* do special things for Feature::A */ 
     /* do something required for every feature */ 
     return true; 
    } 
}; 

// Specialize for feature B only 
template <> 
class Driver<Feature::B> { 
    bool set (uint32_t value) { 
     /* do special things for Feature::B */ 
     /* do something required for every feature */ 
     return true; 
    } 
}; 

// Specialize for features A and B 
template <> 
class Driver<Feature::A | Feature::B> { 
    bool set (uint32_t value) { 
     /* do special things for Feature::A */ 
     /* do special things for Feature::B */ 
     /* do something required for every feature */ 
     return true; 
    } 
}; 

模板方法的好处是,你没有运行时开销由于如果某些功能被启用检查。当然,如果有很多功能,将会有大量的功能组合;因此输出二进制的膨胀。这就是为什么我建议摆脱类模板并使用运行时函数参数;例如,将特征传递给构造函数:

class Driver { 
private: 
    uint32_t features_; 

public: 
    Driver(uint32_t features) : features_(features) {} 
    bool set (uint32_t value) { 
     /* do something required for every feature */ 
     if (features_ & Feature::A) 
     /* do special things if Feature::A */ 
     /* do something required for every feature */ 
     if (features & Feature::B || features & Feature::C) 
     /* do something special for either Feature::B or Feature::C */ 
     return true; 
    } 
}; 
+0

我看不到您的第一个解决方案对于我的解决方案的好处。在不断的折叠之后,模板实例化的代码将是相同的 - 随着回退,公共部分必须通过解决方案多次写入(在每个主体中)。随着未来的变化,人们可能会错过一个可能引入问题/错误的机构。 模板用于减少运行时间开销,因为我知道每次调用时可以使用哪些功能。我有*一个*检查在开始选择正确的控制流程。 问题是,为什么'gcc'不合并相同的代码部分。 – Jonas

+0

你说得对。这就是为什么你不需要模板,而应该使用第二种解决方案。我刚刚举了一个例子,说明如何使用模板*,如果你决定需要它们的话。 –

+0

使用第二种解决方案,我介绍了不需要/不需要的运行时检查。我现在已经在编译时调用了一个函数,这个函数具有我可用的功能,所以这应该是可能的(或者也是可能的)。仍然存在的问题是,我怎样才能使'gcc'结合模板实例产生相同的代码。例如。 '驱动程序 :: set'和'驱动程序 :: set'将是相同的,但会发出两次 - 导致两倍的大小。 对于你说我应该使用模板的方式:与我的解决方案相比,你的解决方案有什么好处(以便我将使用你的解决方案)? – Jonas