将定义的函数片段更改为可测试函数,如何传递参数?

时间:2014-08-20 18:42:44

标签: c++ macros c-preprocessor

我正在尝试重构一些代码,以使其可测试。其中很大一部分使用#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但是无法使其工作......如何将给定的部分转换为实际的函数,而不是宏?

4 个答案:

答案 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,允许将频道称为RGB和数组。然后用通道写一个枚举(实际上用作数组的索引):

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);

除此之外:我会切换到使用引用而不是指针,但我尽量保持与原始方法尽可能接近。