http://blog.csdn.net/insectC/article/details/5310991
在gsl中,由于有float、double、long double多种数据精度,因此如果针对每一种数据精度都分别实现诸如内存分配等函数,代码量将骤增数倍。因此各种数据类型共用这些代码就显得相当重要。gsl利用c语言的预编译命令实现了这种代码的重用功能。
这里我们以最为基础的gsl_block_alloc函数模板为例,剖析gsl代码重用的原理。
下面给出该函数的模板源代码
- 【block/init_souce.c】
- ......
- TYPE (gsl_block) *
- FUNCTION (gsl_block, alloc) (const size_t n)
- {
- TYPE (gsl_block) * b;
- ......
- b = (TYPE (gsl_block) *) malloc (sizeof (TYPE (gsl_block)));
- ......
- b->data = (ATOMIC *) malloc (MULTIPLICITY * n * sizeof (ATOMIC));
- ......
- b->size = n;
- return b;
- }
在《gsl数据类型之向量http://blog.csdn.net/insectC/archive/2010/01/27/5262444.aspx》一文中,我们指出对于double精度,函数中宏的值为:
- 【templates_on.h】
- ......
- #define ATOMIC double
- #define MULTIPLICITY 1
- ......
- #define CONCAT2x(a,b) a ## _ ## b
- #define CONCAT2(a,b) CONCAT2x(a,b)
- ......
- #define FUNCTION(dir,name) CONCAT2(dir,name)
- #define TYPE(dir) dir
- ......
编译后将得到gsl_block_alloc函数,其作用是创建一个包含n个double精度数据的数据块。
而实际上,这一函数模板同样也可以编译成gsl_block_float_alloc,gsl_block_long_double_alloc等等,分别用于创建float精度与long double精度的向量。
其中的奥秘就在block/init_source.c与templates_on.h、templates_off.h的互相配合中。让我们来看看block/init.c文件的代码:
- 【block/init.c】
- #include <config.h>
- #include <stdlib.h>
- #include <gsl/gsl_block.h>
- ......
- #define BASE_DOUBLE
- #include "templates_on.h"
- #include "init_source.c"
- #include "templates_off.h"
- #undef BASE_DOUBLE
- #define BASE_FLOAT
- #include "templates_on.h"
- #include "init_source.c"
- #include "templates_off.h"
- #undef BASE_FLOAT
- ......
限于篇幅,其中略去了除float,double外的其他精度类型的代码。
让我们来跟踪编译过程。首先在定义了BASE_DOUBLE后,编译器将引入templates_on.h。头文件中的关键内容如下:
- 【templates_on.h】
- #if defined(BASE_GSL_COMPLEX_LONG)
- ......
- #elif defined(BASE_DOUBLE)
- #define BASE double
- #define SHORT
- #define ATOMIC double
- #define MULTIPLICITY 1
- #define FP 1
- #define IN_FORMAT "%lg"
- #define OUT_FORMAT "%g"
- #define ATOMIC_IO ATOMIC
- #define ZERO 0.0
- #define ONE 1.0
- #define BASE_EPSILON GSL_DBL_EPSILON
- #elif defined(BASE_FLOAT)
- #define BASE float
- #define SHORT float
- #define ATOMIC float
- #define MULTIPLICITY 1
- #define FP 1
- #define IN_FORMAT "%g"
- #define OUT_FORMAT "%g"
- #define ATOMIC_IO ATOMIC
- #define ZERO 0.0F
- #define ONE 1.0F
- #define BASE_EPSILON GSL_FLT_EPSILON
- ......
- #endif
在文件中,ATOMIC被定义为 double,MULTIPLICITY被定义为1。这与前文一致。随后我们又将进一步定义一系列的宏:
- 【templates_on.h】
- ......
- #define CONCAT2x(a,b) a ## _ ## b
- #define CONCAT2(a,b) CONCAT2x(a,b)
- #define CONCAT3x(a,b,c) a ## _ ## b ## _ ## c
- #define CONCAT3(a,b,c) CONCAT3x(a,b,c)
- #define CONCAT4x(a,b,c,d) a ## _ ## b ## _ ## c ## _ ## d
- #define CONCAT4(a,b,c,d) CONCAT4x(a,b,c,d)
- ......
- #if defined(BASE_DOUBLE)
- #define FUNCTION(dir,name) CONCAT2(dir,name)
- #define TYPE(dir) dir
- #define VIEW(dir,name) CONCAT2(dir,name)
- #define QUALIFIED_TYPE(dir) TYPE(dir)
- #define QUALIFIED_VIEW(dir,name) CONCAT2(dir,name)
- #else
- #define FUNCTION(a,c) CONCAT3(a,SHORT,c)
- #define TYPE(dir) CONCAT2(dir,SHORT)
- #define VIEW(dir,name) CONCAT3(dir,SHORT,name)
- #define QUALIFIED_TYPE(dir) TYPE(dir)
- #define QUALIFIED_VIEW(dir,name) CONCAT3(dir,SHORT,name)
- #endif
在这里,FUNCTION(dir,name)宏被定义为CONCAT2(dir,name),而TYPE(dir)宏被定义为dir。
这样,当我们重新回到init.c并进入view_source.c编译时,便顺理成章的得到了gsl_block_alloc等一系列double精度的函数。
现在,编译器只剩下一点扫尾工作,即进入templates_off.h文件,关闭已有宏定义,为下一步float精度的编译做好准备。这里节选代码如下:
- 【templates_off.h】
- #ifdef FUNCTION
- #undef FUNCTION
- #endif
- #ifdef CONCAT4
- #undef CONCAT4
- #endif
- #ifdef CONCAT4x
- #undef CONCAT4x
- #endif
- #ifdef CONCAT3
- #undef CONCAT3
- #endif
- #ifdef CONCAT3x
- #undef CONCAT3x
- #endif
- #ifdef CONCAT2
- #undef CONCAT2
- #endif
- #ifdef CONCAT2x
- #undef CONCAT2x
- #endif
- #ifdef TYPE
- #undef TYPE
- #endif
- ......
- #undef BASE
- #undef BASE_EPSILON
- #undef SHORT
- #undef ATOMIC
- #undef MULTIPLICITY
- #undef IN_FORMAT
- #undef OUT_FORMAT
- #undef ATOMIC_IO
- #undef ZERO
- #undef ONE
- ......
- #ifdef FP
- #undef FP
- #endif
这样double精度的函数编译工作便完成了。按照init.c中的流程,编译器将定义宏BASE_FLOAT,并开始float精度函数的编译。读者可以自己从上面的代码中体会其中的精妙。