使用 C 语言的“准元程序”设计
将 C 语言的预编译语言看成是“元语言”,使用该元语言进行程序设计
(1) not,and,or,xor,…完备的布尔代数,共16种布尔操作,去掉全真和全假,是14种操作
(2) 许多种象素位数(1,2,4,8,16,24,32),甚至更多。
(3) 这些情况组合起来,共有 686种(7×7×14)这显然是一个“过于完备”)的集合。
(4) 如果按普通的方式编码,要写 686多个不同的函数,或者 switch…case中686种不同的 case。而这些代码都是非常相似的,如果把相同部分提取出来,把不同部分使用“模板”替换掉…..
但为什么叫“准元程序”?
因为 C 语言的预编译语言没有迭代结构,所以C 语言的元程序语言不是图灵完备的。举个简单的例子,我们无法用 C 语言的“元语言”写出一个计算 factorial(x)——x 的阶乘的程序;而用 C++的模板就可以(使用模板特化)。因为这需要语言的迭代结构。C预编译语言没有迭代结构的原因是宏替换仅发生在源语言的宏调用中,预编译中的宏调用是不会发生替换的(除了条件编译中条件中对宏的调用)如:
#define macro1(x) (x)*(x) #define macro2(x) macro1(x) // 这里不会发生宏替换 int y = macro2(100); // 宏替换仅发生在这里但是,在条件编译中:
#if macro2(100) == 10000 // 这里会发生宏替换 …. #endif因此,象这样的程序是错误的:
// fac.c, 计算 x 的阶乘 #if x <= 0 # define result 1 #else # undef temp_x # define temp_x (x - 1) # undef x # define x temp_x # include "fac.c" # undef temp_result # define temp_result result * x # undef result # define result temp_result #endif
适应它,Play With Preprocessor
虽然如此,用 C 语言的预编译能力,在很多时候还是可以写出很好的程序,可以尽可能地减少代码冗余,增强程序性能。程序的可读性,也许增加了,也许减少了;但是在这个探索的过程中,很可能对问题的认识更深刻了,问题得到了更高程度的抽象。
元程序使用的几个重要重要指令:
#define macro // 用来定义“模板参数” #undef macro // 用来清除“模板参数” #include "template_body" // 用来定义“模板体” // 符号展开连接: #define CAT_TOKEN_1(t1, t2) t1##t2 #define CAT_TOKEN(t1, t2) CAT_TOKEN_1(t1,t2) // CAT_TOKEN_1 直接将 t1 和 t2 连接成 t1t2,而 // CAT_TOKEN 将 t1 和 t2 展开后再连接,如: #define t1 I_am_ #define t2 lei_peng CAT_TOKEN_1(t1, t2) // 结果是 t1t2 CAT_TOKEN(t1, t2) // 结果是 I_am_leipeng
CAT_TOKEN是一个核心的基本构造块。
总体上讲,C++的元程序设计是函数式语言(类似 lisp),而C语言的元程序设计有点类似汇编语言,试看:// C 元程序代码 #define param1 ….. #define param2 ….. #define param3 ….. #include “template.c” #undef param1 // 这些 #undef 也可以位于 “template.c”之内 #undef param2 #undef param3
// 汇编的函数调用代码 Push param1 Push param2 Push param3 Call function Add esp, 3 * 4 //恢复堆栈,堆栈也可以在 function 的返回语句恢复
实例,实现函数
int bitblt( GdiDevice* dst, const GdiDevice* src , int dx, int dy, int cx, int cy, int sx, int sy , BinaryOpCode op );a) 这是一个性能要求相当高基本图像位传送函数,同时又有许多种位操作:
(1) not,and,or,xor,…完备的布尔代数,共16种布尔操作,去掉全真和全假,是14种操作
(2) 许多种象素位数(1,2,4,8,16,24,32),甚至更多。
(3) 这些情况组合起来,共有 686种(7×7×14)这显然是一个“过于完备”)的集合。
(4) 如果按普通的方式编码,要写 686多个不同的函数,或者 switch…case中686种不同的 case。而这些代码都是非常相似的,如果把相同部分提取出来,把不同部分使用“模板”替换掉…..
(5) 详细内容见代码,代码是非常短的(同时还有另外一个函数mergeblt,原型与bitblt相同,其中仅实现了16,24,32位象素),这些代码如果使用预编译器输出处理结果,有 6000 多行!:
代码(附件中有完整代码)
// 模板体,bitblt_body.c ////////////////////////////////////////////////////////////////////////// MCASE(dstBits, srcBits, bitOpCode) { callerVars() int dstRowBytes = ROW_BYTES_2(dstBits, dst); int srcRowBytes = ROW_BYTES_2(srcBits, src); int i; PBYTE dstRow = dst->data + dy * dstRowBytes + PIX_POS_2(dstBits, dx); PBYTE srcRow = src->data + sy * srcRowBytes + PIX_POS_2(srcBits, sx); for (i = cy; i; --i) { PBYTE dstCol = dstRow; PBYTE srcCol = srcRow; int j; for (j = cx; j; --j) { condition(dstCol, srcCol) callBitOp(dstCol, srcCol); } dstRow += dstRowBytes; srcRow += srcRowBytes; } } return 0; #undef bitOpCode #undef doBitOp // 相同象素位的不同位操作(14种) #define bitOpCode opCopy #define doBitOp(d, s) d = s #include "bitblt_body.c" #define bitOpCode opAnd #define doBitOp(d, s) d &= s #include "bitblt_body.c" #define bitOpCode opOr #define doBitOp(d, s) d |= s #include "bitblt_body.c" #define bitOpCode opXor #define doBitOp(d, s) d ^= s #include "bitblt_body.c" #define bitOpCode opNotSrc #define doBitOp(d, s) d = ~s #include "bitblt_body.c" #define bitOpCode opNotAnd #define doBitOp(d, s) d = ~(d & s) #include "bitblt_body.c" #define bitOpCode opNotOr #define doBitOp(d, s) d = ~(d | s) #include "bitblt_body.c" #define bitOpCode opNotXor #define doBitOp(d, s) d = ~(d ^ s) #include "bitblt_body.c" #define bitOpCode opNotDestAnd #define doBitOp(d, s) d = ~d & s #include "bitblt_body.c" #define bitOpCode opNotDestOr #define doBitOp(d, s) d = ~d | s #include "bitblt_body.c" #define bitOpCode opNotDestXor #define doBitOp(d, s) d = ~d ^ s #include "bitblt_body.c" #define bitOpCode opNotSrcAnd #define doBitOp(d, s) d &= ~s #include "bitblt_body.c" #define bitOpCode opNotSrcOr #define doBitOp(d, s) d |= ~s #include "bitblt_body.c" #define bitOpCode opNotSrcXor #define doBitOp(d, s) d ^= ~s #include "bitblt_body.c" #undef callBitOp #undef dstBits #undef srcBits #undef pSrcToColor // blt_body.c, 所有象素位数的模板,这里只定义了 16,24,32三种象素的相互操作 // dst --- 16 #define dstBits 16 #define srcBits 16 #define pSrcToColor PCOLOR16_TO_32 #define callBitOp(pd, ps) doBitOp(*(UINT16*)pd,*(UINT16*)ps), pd+=2,ps+=2 #include "bitblt_op.c" #define dstBits 16 #define srcBits 24 #define pSrcToColor PCOLOR24_TO_32 #define callBitOp(pd, ps) doBitOp(*(UINT16*)pd, PCOLOR24_TO_16(ps)), \ pd +=2, ps += 3 #include "bitblt_op.c" #define dstBits 16 #define srcBits 32 #define pSrcToColor(x) *(x) #define callBitOp(pd, ps) doBitOp(*(UINT16*)pd, PCOLOR32_TO_16(ps)), \ pd +=2, ps += 4 #include "bitblt_op.c" // dst --- 24 #define dstBits 24 #define srcBits 16 #define pSrcToColor PCOLOR16_TO_32 #define callBitOp(pd, ps) doBitOp(pd[0], C16_R(*(UINT16*)ps)), \ doBitOp(pd[1], C16_G(*(UINT16*)ps)), \ doBitOp(pd[2], C16_B(*(UINT16*)ps)), \ pd +=3, ps += 2 #include "bitblt_op.c" #define dstBits 24 #define srcBits 24 #define pSrcToColor PCOLOR24_TO_32 #define callBitOp(pd, ps) doBitOp(pd[0], ps[0]), \ doBitOp(pd[1], ps[1]), \ doBitOp(pd[2], ps[2]), pd +=3, ps += 3 #include "bitblt_op.c" #define dstBits 24 #define srcBits 32 #define pSrcToColor(x) *(x) #define callBitOp(pd, ps) doBitOp(pd[0], C32_R(*ps)), \ doBitOp(pd[1], C32_G(*ps)), \ doBitOp(pd[2], C32_B(*ps)), pd +=3, ps += 4 #include "bitblt_op.c" // dst --- 32 #define dstBits 32 #define srcBits 16 #define pSrcToColor PCOLOR16_TO_32 #define callBitOp(pd, ps) doBitOp(*(UINT32*)pd, PCOLOR16_TO_32(ps)), \ pd +=4, ps += 2 #include "bitblt_op.c" #define dstBits 32 #define srcBits 24 #define pSrcToColor PCOLOR24_TO_32 #define callBitOp(pd, ps) doBitOp(*(UINT32*)pd, PCOLOR24_TO_32(ps)), \ pd +=4, ps += 3 #include "bitblt_op.c" #define dstBits 32 #define srcBits 32 #define pSrcToColor(x) *(x) #define callBitOp(pd, ps) doBitOp(*(UINT32*)pd,*(UINT32*)ps), pd+=4, ps+=4 #include "bitblt_op.c" // 函数体,适用于 bitblt 和 mergeblt,模板参数为 condition static const unsigned char jump_table[] = { 0, bit_1, bit_2, 0, bit_4 , 0, 0, 0, bit_8 , 0, 0, 0, bit_12, 0, 0, 0, bit_16, 0, 0, 0, 0, 0, 0, 0, bit_24, 0, 0, 0, 0, 0, 0, 0, bit_32 }; int jump_index; if (dst->colorBits > 32) return -1; if (src->colorBits > 32) return -2; if ((unsigned int)op >= (unsigned int)BinaryOpCode_Radix) return -4; jump_index = jump_table[dst->colorBits] + jump_table[src->colorBits] * bitRadix + op * bitRadix*bitRadix; switch (jump_index) { default: if (0 == jump_table[dst->colorBits]) return -1; // dst->colorBits error else if (0 == jump_table[src->colorBits]) return -2; // src->colorBits error else return -3; // not support #include "blt_body.c" } return -5; // it will not goes here #undef condition // gdi.h 定义图像基本类型和常量,及 utilities #ifndef __GDI_H__ #define __GDI_H__ typedef enum BinaryOpCode { opCopy, // dst = src opXor, // dst = src ^ dst opAnd, // dst = src & dst opOr, // dst = src | dst opNotSrc, // dst = ~src opNotAnd, // dst = ~(src & dst) opNotOr, // dst = ~(src | dst) opNotXor, // dst = ~(src ^ dst) opNotDestAnd, // dst = ~dst & src opNotDestOr, // dst = ~dst | src opNotDestXor, // dst = ~dst ^ src opNotSrcAnd, // dst = dst & ~src opNotSrcOr, // dst = dst | ~src opNotSrcXor, // dst = dst ^ ~src // opSet, // dst = 1 // opClear, // dst = 0 BinaryOpCode_Radix } BinaryOpCode; typedef unsigned char BYTE, *PBYTE; typedef unsigned long COLOR; typedef unsigned short UINT16; typedef unsigned long UINT32; typedef struct _GdiDevice { unsigned int colorBits; int width; int height; COLOR transparentColor; int transparentTolerance; COLOR* pallete; PBYTE data; } GdiDevice; #define ROW_BYTES_2(colorBits, gdi) ((7 + (gdi)->width * colorBits) >> 3) #define PIX_POS_2(colorBits, x) ((x * colorBits) >> 3) #define ROW_BYTES(gdi) ((7 + (gdi)->width * (gdi)->colorBits) >> 3) #define PIX_POS(gdi, x) ((x * (gdi)->colorBits) >> 3) #endif // bitblt.c // bitblt 函数和 mergeblt的函数体 #include "gdi.h" enum ColorBitKind { bitError = 0, bit_1 = 1, bit_2 = 2, bit_4, bit_8, bit_12, bit_16, bit_24, bit_32, bitRadix }; #define CAT_TOKEN1(t1, t2) t1##t2 #define CAT_TOKEN(t1, t2) CAT_TOKEN1(t1, t2) #define MCASE(dstBits, srcBits, bitOpCode) \ case CAT_TOKEN(bit_, dstBits) + \ CAT_TOKEN(bit_, srcBits)*bitRadix + bitOpCode*bitRadix*bitRadix: // 00RR-RRRR-RRRR-GGGG-GGGG-GGBB-BBBB-BBBB // 0x3FF00000 0x000FFC00 0x000003FF #define C32_R(c) (c >> 20 & 0x3FF) #define C32_G(c) (c >> 10 & 0x3FF) #define C32_B(c) (c & 0x3FF) #define C16_R(c) (c >> 10 & 0x1F) #define C16_G(c) (c >> 5 & 0x1F) #define C16_B(c) (c & 0x1F) #define PCOLOR24_TO_16(p24) (\ ((UINT16)p24[2]&0xF8) << 8 |\ ((UINT16)p24[1]&0xF8) << 3 |\ p24[0] >> 3) // 这几种颜色转换未实现 #define PCOLOR32_TO_16(p32) 0 #define PCOLOR16_TO_24(p16) 0 #define PCOLOR32_TO_24(p16) 0 #define PCOLOR16_TO_32(p24) 0 #define PCOLOR24_TO_32(p24) 0 // empty... #define callerVars() int bitblt(GdiDevice* dst, const GdiDevice* src, int dx, int dy, int cx, int cy, int sx, int sy, BinaryOpCode op) { #define condition(pd, ps) #include "template/fun_body.c" } int mergeblt(GdiDevice* dst, const GdiDevice* src, int dx, int dy, int cx, int cy, int sx, int sy, BinaryOpCode op) { // condition no tolerance... #define condition(pd, ps) if (pSrcToColor(ps) != src->transparentColor) #include "template/fun_body.c" }