C++ Inlinine函数指针和模板函数设计
C++ Inlinine function pointers and template function design
好的,我有一个类允许用户在100x64 LCD上设置像素。
// (U8 = unsigned char)
inline void pixelOn(const U8 X, const U8 Y) {
*(disp + ((Y / LCD_DEPTH) * LCD_WIDTH + X)) |= (1 << (Y % LCD_DEPTH));
}
inline void pixelOff(const U8 X, const U8 Y) {
*(disp + ((Y / LCD_DEPTH) * LCD_WIDTH + X)) &= !(1 << (Y % LCD_DEPTH));
}
现在我有一个在液晶屏上画一条线的类。有3个函数使用不同的像素"设置功能":显示,erease,反转
void NLine::show(bool update) const {
if(lcd == 0) return;
if (!NLcd::pixelInLcd(x, y) && !NLcd::pixelInLcd(endX, endY))
return;
if ((x == endX) || (y == endY)) {
straight(&NLcd::pixelOn);
} else {
bresenham(&NLcd::pixelOn);
}
visible = true;
if (update) {
display_update();
}
}
目前调用一个私有函数来使用函数指针设置像素。
void NLine::bresenham(void (NLcd::*fpPixelState)(const U8, const U8)) const {
// predetermine function to avoid ifs during calculation!
// low level pixel functions use U8!
S8 ix = x;
S8 iy = y;
S8 sx = ix < endX ? 1 : -1;
S8 sy = iy < endY ? 1 : -1;
S16 err = width + (-height), e2;
for (;;) {
// how to get the compiler to inline this (template?)!
(lcd->*fpPixelState)(static_cast<U8>(ix), static_cast<U8>(iy));
if (ix == endX && iy == endY)
break;
e2 = 2 * err;
if (e2 > (-height)) {
err += (-height);
ix += sx;
}
if (e2 < width) {
err += width;
iy += sy;
}
}
}
我认为我希望编译器在 for 循环中内联此函数是可以理解的。我试图用模板解决这个问题,但存在同样的问题,我不知道编译器是否使用内联。我应该使用完全不同的设计还是如何解决这个问题?下一个问题是,如果我调用 show erase 和反转,编译器会生成很多代码,因为内联不同,所以我认为我应该使用不同的代码设计吗?
编辑:
首先,感谢迪特马尔·库尔的设计建议!所以这是结论:
这是测试代码:
NLine line(lcd, 0, 0, 99, 0);
t0 = timer.now();
for(S8 i=0; i<NLcd::LCD_HEIGHT; ++i) {
// x0, y0, xend, yend
line.setPosition(NLine::keep, i, NLine::keep, i);
// call only straight not the bigger bresenham function
line.show();
line.erase();
line.invert();
}
t1 = timer.now();
方法 1:使用功能指针(第一个)
内存:26384
室温:51 ms
方法 2:使用函数对象 (Dietmar Kühl)
内存:26592
室温:27 ms
方法三:使用切换入 for 循环确定像素运算函数
内存:26416
室温:36 ms
方法2:最佳RT,但事实证明,如果实现,程序会变得非常大周围的绘图方法变得非常大,尤其是对于布雷森汉姆。发生这种情况是因为模板实现为所有 3 像素函数生成了完整的代码。
方法3:似乎最简单的方法是一个很好的权衡。
欢迎提出进一步的建议。
您不能将一个函数作为指向另一个函数的指针传递,并期望编译器内联它,除非代码真的很琐碎(这样编译器可以内联函数指针传递给的函数)。编译器(通常)不会知道您不会在其他上下文中传递其他函数指针(例如在不同的编译单元中),因此,它仍然必须"工作"函数指针就地。
可能有几种不同的方法可以"修复"这个问题,如果它真的那么重要 - 但是调用函数[通过指针或不通过指针]的开销可能非常小,而不是你正在做的数学(除以LCD_DEPTH是昂贵的)。您是否真的进行了测量以确定呼叫时间是否产生了差异?
另外,您的pixelOff
代码可能是错误的,我希望:
*(disp + ((Y / LCD_DEPTH) * LCD_WIDTH + X)) &= !(1 << (Y % LCD_DEPTH));
应该是:
*(disp + ((Y / LCD_DEPTH) * LCD_WIDTH + X)) &= ~(1 << (Y % LCD_DEPTH));
因为这会与
最有效的方法不是传入函数指针,而是使用指定函数对象的模板参数自定义函数。然后,您可以使用内联函数调用运算符传入函数对象(显然,成员函数需要在 NLine
clas 中相应地声明:
template <typename PixelState>
// PixelState is a function object taking
// - a reference to an `NLcd`
// - two U8 parameters and returns nothing
void NLine::bresenham(PixelState pixelState) const {
// ...
pixelState(lcd,static_cast<U8>(ix), static_cast<U8>(iy));
// ...
}
然后,相应的函数对象可以是具有合适函数调用运算符的类,例如:
struct PixelOn
{
void operator()(NLcd& lcd, U8 x, U8 y) const {
lcd.pixelOn(x, y);
}
};
// ...
bresenham(PixelOn());
// with C++ as of the 2011 revision:
bresenham([](Nlcd& lcd, U8 x, U8 y){ lcd.pixelOn(x, y); });
像这样的函数对象通常可以由编译器内联,编译器往往也擅长内联它们,因为这种技术经常使用,例如,与标准C++算法一起使用。您的特定函数是否实际内联取决于编译器是否决定这样做,但它通常会做出最有效的选择(当然,假设合理的优化级别)。如果NLcd::pixelOn
可以内联,则此函数也将内联。
- "error: no matching function for call to"构造函数错误
- 什么时候调用组成单元对象的析构函数
- 继承函数的重载解析
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- C++模板来检查友元函数的存在
- 递归函数计算序列中的平方和(并输出过程)
- 对RValue对象调用的LValue ref限定成员函数
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 为什么使用 "this" 指针调用派生成员函数?
- 将对象数组的引用传递给函数
- 函数调用中参数的顺序重要吗
- 函数向量_指针有不同的原型,我可以构建一个吗
- 使用不带参数的函数访问结构元素
- 代码在main()中运行,但在函数中出现错误
- 内置函数可查看CPP中的成员变量
- 如何获取std::result_of函数的返回类型
- 如何在c++中为模板函数实例创建快捷方式
- C++ Inlinine函数指针和模板函数设计