使用 C 语言的“准元程序”设计
将 C 语言的预编译语言看成是“元语言”,使用该元语言进行程序设计
但为什么叫“准元程序”?
因为 C 语言的预编译语言没有迭代结构,所以C 语言的元程序语言不是图灵完备的。举个简单的例子,我们无法用 C 语言的“元语言”写出一个计算 factorial(x)——x 的阶乘的程序;而用 C++的模板就可以(使用模板特化)。因为这需要语言的迭代结构。C预编译语言没有迭代结构的原因是宏替换仅发生在源语言的宏调用中,预编译中的宏调用是不会发生替换的(除了条件编译中条件中对宏的调用)如:
1 2 3 |
#define macro1(x) (x)*(x) #define macro2(x) macro1(x) // 这里不会发生宏替换 int y = macro2(100); // 宏替换仅发生在这里 |
但是,在条件编译中:
1 2 3 |
#if macro2(100) == 10000 // 这里会发生宏替换 …. #endif |
因此,象这样的程序是错误的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// 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 语言的预编译能力,在很多时候还是可以写出很好的程序,可以尽可能地减少代码冗余,增强程序性能。程序的可读性,也许增加了,也许减少了;但是在这个探索的过程中,很可能对问题的认识更深刻了,问题得到了更高程度的抽象。
元程序使用的几个重要重要指令:
1 2 3 4 5 6 7 8 9 10 11 12 |
#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语言的元程序设计有点类似汇编语言,试看:
1 2 3 4 5 6 7 8 |
// C 元程序代码 #define param1 ….. #define param2 ….. #define param3 ….. #include “template.c” #undef param1 // 这些 #undef 也可以位于 “template.c”之内 #undef param2 #undef param3 |
1 2 3 4 5 6 |
// 汇编的函数调用代码 Push param1 Push param2 Push param3 Call function Add esp, 3 * 4 //恢复堆栈,堆栈也可以在 function 的返回语句恢复 |
实例,实现函数
1 2 3 4 |
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 多行!:
代码(附件中有完整代码)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
// 模板体,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" } |
呵呵,你倒是让预处理器挑起了代码产生器的重担。
<br>
<br> 元编程技术很有趣,从早期的Lisp/Scheme可编程宏到C++的Template,再到REBOL语言的Dialect概念都通过不同方式为语言提供了这么一种能力。
<br>
<br> 本人也对这方面颇有兴趣,目前正在开发一个支持静态元编程的开放式Lua语言编译器: http://www.luachina.org/projects/openlua/
<br>
<br> 希望能与你深入交流。
有人发信抱怨排版太乱,重新排过