我正在测试各种优化的组合,对于这些我需要静态 - 如http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/Static-If-I-Had-a-Hammer中所述,以启用和禁用特定优化。 if(const-expr)并不总是有效,因为某些优化涉及更改数据布局,而这在功能范围内无法完成。
基本上我想要的是:
template<bool enable_optimization>
class Algo{
struct Foo{
int a;
if(enable_optimization){
int b;
}
void bar(){
if(enable_optimization){
b = 0;
}
}
};
};
(是的,在我的情况下,从数据布局中删除b的较小内存占用量是相关的。)
目前我使用非常糟糕的黑客伪造它。我正在寻找一种更好的方法。
档案a.h
#ifndef A_H
#define A_H
template<class enable_optimization>
class Algo;
#include "b.h"
#endif
文件b.h(此文件是从Python脚本自动生成的)
#define ENABLE_OPTIMIZATION 0
#include "c.h"
#undef
#define ENABLE_OPTIMIZATION 1
#include "c.h"
#undef
文件c.h
template<>
class Algo<ENABLE_OPTIMIZATION>{
struct Foo{
int a;
#if ENABLE_OPTIMIZATION
int b;
#endif
void bar(){
#if ENABLE_OPTIMIZATION
b = 0;
#endif
}
};
};
有谁知道更好的方法吗?从理论上讲,它可以使用模板元编程完成,起初我使用它。至少我使用它的方式是屁股的痛苦,并导致完全不可读和臃肿的代码。使用上面的hack可以显着提高生产力。
编辑:我有几个优化标志,这些标志相互作用。
答案 0 :(得分:6)
没有理由使用模板使代码变得复杂得多:
template<bool enable_optimization>
class FooOptimized
{
protected:
int b;
void bar_optim()
{
b = 0;
}
};
template<>
class FooOptimized<false>
{
protected:
void bar_optim() { }
};
template<bool enable_optimization>
struct Algo
{
struct Foo : FooOptimized<enable_optimization>
{
int a;
void bar()
{
this->bar_optim();
}
};
};
不需要进行元编程,只需根据优化是否已启用为新类型并将其专门化而将各个部分分开。
因为新类型在空时被用作基类(即没有FooOptimized::b
成员),所以它不会占用空间,因此sizeof(Algo<false>::Foo) == sizeof(int)
。
(随意忽略这个答案的其余部分,它没有直接解决问题,而是提出了一种不同的方法,它有不同的权衡。是否“更好”或更不完全取决于细节真实的代码,在问题中给出的简单示例中没有显示。)
作为一个相关但又独立的问题,不的Algo
和Algo::Foo
部分取决于是否启用优化仍取决于模板参数,因此,虽然您只编写一次这些代码,但编译器将生成两组目标代码。根据代码中的工作量以及它的使用方式,您可能会发现将其更改为非模板代码是有利的,即用动态多态替换静态多态。例如,您可以将enable_optimization
标志设置为运行时构造函数参数而不是模板参数:
struct FooImpl
{
virtual void bar() { }
};
class FooOptimized : FooImpl
{
int b;
void bar()
{
b = 0;
}
};
struct Algo
{
class Foo
{
std::unique_ptr<FooImpl> impl;
public:
explicit
Foo(bool optimize)
: impl(optimize ? new FooOptimized : new FooImpl)
{ }
int a;
void bar()
{
impl->bar();
}
};
};
您必须对其进行分析和测试,以确定虚拟函数的开销是否比Algo
和Algo::Foo
中不依赖于模板参数的代码重复更少。
答案 1 :(得分:1)
注意:就目前而言,这种方法不起作用,因为如果没有为该成员分配空间,似乎无法在类中拥有成员。如果您知道如何使其工作,请随时编辑。
你可以使用这样的成语:
template<bool optimized, typename T> struct dbg_obj {
struct type {
// dummy operations so your code will compile using this type instead of T
template<typename U> type& operator=(const U&) { return *this; }
operator T&() { return *static_cast<T*>(0); /* this should never be executed! */ }
};
};
template<typename T> struct dbg_obj<false, T> {
typedef T type;
};
template<bool enable_optimization>
class Algo{
struct Foo{
int a;
typename dbg_obj<enable_optimization, int>::type b;
void bar(){
if(enable_optimization){
b = 0;
}
}
};
};
如果禁用优化,则会为您提供正常的int
成员。如果启用了优化,则b
的类型是没有成员的结构,不会占用任何空间。
由于您的方法bar
使用看起来像运行时if
的内容来决定是否访问b
,而不是像模板专门化那样清晰的编译时机制,所有您在b
上使用的操作也必须可以从虚拟结构中获得。即使相关部分永远不会被执行,并且编译器很可能将它们优化掉,但首先是正确性检查。因此,行b = 0
也必须为替换类型进行编译。这就是虚拟分配和虚拟投射操作的原因。虽然两者都足以满足您的代码要求,但我还是将它们包括在内,以防它们在某些其他方面证明是有用的,并且如果您需要它们,还可以了解如何添加更多代码。
答案 2 :(得分:0)
Jonathan Wakely提出的静态解决方案(不是动态解决方案)是要走的路。
这个想法很简单:
然后,为了不产生空间开销,您可以通过继承此特殊类的特化来使用EBO(空基优化)。
注意:需要一个属性来占用至少1个字节的空间,基类不在某些条件下(例如为空)。
在你的情况下:
b
是具体数据所以我们可以轻松地构建一个类:
template <typename T> struct FooDependent;
template <>
struct FooDependent<true> {
int b;
void set(int x) { b = x; }
};
template <>
struct FooDependent<false> {
void set(int) {}
};
然后我们可以使用EBO将其注入Foo
:
struct Foo: FooDependent<enable_optimization> {
int a;
void bar() { this->set(0); }
};
注意:在模板化代码中使用this
来访问基类的成员,或者好的编译器会拒绝您的代码。