我正在尝试重构一些代码,以使其可测试。其中很大一部分使用#define来填充函数中的重复代码。我试图在实际功能中改变它,但我遇到了绊脚石。
注意:原始代码来自dithering algorithm
typedef struct{
unsigned char R, G, B;
} RGBTriple;
#define compute_disperse(channel) \
error = ((int)(currentPixel->channel)) - palette.table[index].channel; \
image.pixels[(x+1) + (y+0)*image.width].channel += (error*7) >> 4; \
image.pixels[(x-1) + (y+1)*image.width].channel += (error*3) >> 4; \
image.pixels[(x+0) + (y+1)*image.width].channel += (error*5) >> 4; \
image.pixels[(x+1) + (y+1)*image.width].channel += (error*1) >> 4;
在实际方法中
RGBTriple* currentPixel = &(image.pixels[x + y*image.width]);
compute_disperse(R);
compute_disperse(G);
compute_disperse(B);
如果我尝试将compute_disperse转换为实际函数,那么我需要以某种方式将“channel”作为R,G或B的值传递。
我试图将其作为unsigned char传递,但是我收到了错误
error: 'struct RGBTriple' has no member named 'channel'
我读到了定义R G和B但是无法使其工作......如何将给定的部分转换为实际的函数,而不是宏?
答案 0 :(得分:7)
作为Slava回答的后续内容,可以使用成员指针...作为模板参数!
template <unsigned char RGBTriple::*Tchannel>
compute_disperse(/* Whatever you need */) {
error = ((int)(currentPixel->*Tchannel)) - palette.table[index].*Tchannel;
/* ... */
}
任何体面的编译器都可以进行最少的输入,灵活且完全内联。
答案 1 :(得分:2)
明显的解决方案是将指针传递给成员unsigned char RGBTriple::*channel
,但我敢打赌,这会产生性能问题。我会改用模板:
enum ChannelName { R, G, B };
template <ChannelName>
unsigned char &getChannel( RGBTriple &rgb );
template <>
unsigned char &getChannel<R>( RGBTriple &rgb ) { return rgb.R; }
template <>
unsigned char &getChannel<G>( RGBTriple &rgb ) { return rgb.G; }
template <>
unsigned char &getChannel<B>( RGBTriple &rgb ) { return rgb.B; }
然后
template <ChannelName ch>
compute_disperse( /* parameters, probably currentPixel, pallette and image */ ) {
error = ((int)getChannel<ch>( *currentPixel ) - getChannel<ch>( palette.table[index] );
getChannel<ch>( image.pixels[(x+1) + (y+0)*image.width] ) += (error*7) >> 4;
...
}
然后您将以这种方式致电compute_disperse
:
compute_disperse<R>( /* parameters */ );
compute_disperse<G>( /* parameters */ );
compute_disperse<B>( /* parameters */ );
您需要为该函数提供哪些参数超出了本答案的范围,您需要分析此函数访问哪些数据。
答案 2 :(得分:1)
为RGB类型使用union,允许将频道称为R
,G
,B
和数组。然后用通道写一个枚举(实际上用作数组的索引):
struct RGB
{
union
{
struct { unsigned char R , G , B };
unsigned char channels[3];
};
};
enum rgb_channel{ R = 0 , G = 1 , B = 2 };
以下是一个例子:
unsigned char get_channel_value( RBG color , rgb_channel channel )
{
return color.channels[(int)channel]; //The cast is not strictly necessary, but
//makes the code more concise.
}
//You could do this:
color.R = 0;
//Or this:
color.channels[R] = 0;
答案 3 :(得分:1)
这没有改变:
typedef struct{
unsigned char R, G, B;
} RGBTriple;
键入一个访问者函数:
typedef char & Accessor(RGBTriple *);
为每个频道定义访问者功能
char & AccessR(RGBTriple * pixel) { return pixel->R;}
char & AccessG(RGBTriple * pixel) { return pixel->G;}
char & AccessB(RGBTriple * pixel) { return pixel->B;}
这是你的新compute_disperse函数(不再是宏)
void compute_disperse(RGBTriple * currentPixel, Accessor access)
{
int error = ((int)access(currentPixel) - access(palette.table[index]);
access(image.pixels[(x+1) + (y+0)*image.width]) += (error*7) >> 4;
access(image.pixels[(x-1) + (y+1)*image.width]) += (error*3) >> 4;
access(image.pixels[(x+0) + (y+1)*image.width]) += (error*5) >> 4;
access(image.pixels[(x+1) + (y+1)*image.width]) += (error*1) >> 4;
}
以下是你如何使用它:
RGBTriple* currentPixel = &(image.pixels[x + y*image.width]);
compute_disperse(currentPixel, AccessR);
compute_disperse(currentPixel, AccessG);
compute_disperse(currentPixel, AccessB);
除此之外:我会切换到使用引用而不是指针,但我尽量保持与原始方法尽可能接近。