gsl中的代码重用技术

http://blog.csdn.net/insectC/article/details/5310991

     在gsl中,由于有float、double、long double多种数据精度,因此如果针对每一种数据精度都分别实现诸如内存分配等函数,代码量将骤增数倍。因此各种数据类型共用这些代码就显得相当重要。gsl利用c语言的预编译命令实现了这种代码的重用功能。

      这里我们以最为基础的gsl_block_alloc函数模板为例,剖析gsl代码重用的原理。

     下面给出该函数的模板源代码

    

[cpp]  view plain copy
  1. 【block/init_souce.c】  
  2. ......  
  3. TYPE (gsl_block) *  
  4. FUNCTION (gsl_block, alloc) (const size_t n)  
  5. {  
  6.   TYPE (gsl_block) * b;  
  7.   ......  
  8.   b = (TYPE (gsl_block) *) malloc (sizeof (TYPE (gsl_block)));  
  9.   ......  
  10.   b->data = (ATOMIC *) malloc (MULTIPLICITY * n * sizeof (ATOMIC));  
  11.   ......  
  12.   b->size = n;  
  13.   return b;  
  14. }  
 

    在《gsl数据类型之向量http://blog.csdn.net/insectC/archive/2010/01/27/5262444.aspx》一文中,我们指出对于double精度,函数中宏的值为:

[cpp]  view plain copy
  1. 【templates_on.h】  
  2. ......  
  3. #define ATOMIC double  
  4. #define MULTIPLICITY 1  
  5. ......  
  6. #define CONCAT2x(a,b) a ## _ ## b   
  7. #define CONCAT2(a,b) CONCAT2x(a,b)  
  8. ......  
  9. #define FUNCTION(dir,name) CONCAT2(dir,name)  
  10. #define TYPE(dir) dir  
  11. ......  
 

      编译后将得到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文件的代码:

 

[cpp]  view plain copy
  1. 【block/init.c】  
  2. #include <config.h>  
  3. #include <stdlib.h>  
  4. #include <gsl/gsl_block.h>  
  5. ......  
  6. #define BASE_DOUBLE  
  7. #include "templates_on.h"  
  8. #include "init_source.c"  
  9. #include "templates_off.h"  
  10. #undef  BASE_DOUBLE  
  11. #define BASE_FLOAT  
  12. #include "templates_on.h"  
  13. #include "init_source.c"  
  14. #include "templates_off.h"  
  15. #undef  BASE_FLOAT  
  16. ......  
 

      限于篇幅,其中略去了除float,double外的其他精度类型的代码。

      让我们来跟踪编译过程。首先在定义了BASE_DOUBLE后,编译器将引入templates_on.h。头文件中的关键内容如下:

[cpp]  view plain copy
  1. 【templates_on.h】  
  2. #if defined(BASE_GSL_COMPLEX_LONG)  
  3. ......  
  4. #elif defined(BASE_DOUBLE)  
  5. #define BASE double  
  6. #define SHORT  
  7. #define ATOMIC double  
  8. #define MULTIPLICITY 1  
  9. #define FP 1  
  10. #define IN_FORMAT "%lg"  
  11. #define OUT_FORMAT "%g"  
  12. #define ATOMIC_IO ATOMIC  
  13. #define ZERO 0.0  
  14. #define ONE 1.0  
  15. #define BASE_EPSILON GSL_DBL_EPSILON  
  16. #elif defined(BASE_FLOAT)  
  17. #define BASE float  
  18. #define SHORT float  
  19. #define ATOMIC float  
  20. #define MULTIPLICITY 1  
  21. #define FP 1  
  22. #define IN_FORMAT "%g"  
  23. #define OUT_FORMAT "%g"  
  24. #define ATOMIC_IO ATOMIC  
  25. #define ZERO 0.0F  
  26. #define ONE 1.0F  
  27. #define BASE_EPSILON GSL_FLT_EPSILON  
  28. ......  
  29. #endif  
 

在文件中,ATOMIC被定义为 double,MULTIPLICITY被定义为1。这与前文一致。随后我们又将进一步定义一系列的宏:

[cpp]  view plain copy
  1. 【templates_on.h】  
  2. ......  
  3. #define CONCAT2x(a,b) a ## _ ## b   
  4. #define CONCAT2(a,b) CONCAT2x(a,b)  
  5. #define CONCAT3x(a,b,c) a ## _ ## b ## _ ## c  
  6. #define CONCAT3(a,b,c) CONCAT3x(a,b,c)  
  7. #define CONCAT4x(a,b,c,d) a ## _ ## b ## _ ## c ## _ ## d  
  8. #define CONCAT4(a,b,c,d) CONCAT4x(a,b,c,d)  
  9. ......  
  10. #if defined(BASE_DOUBLE)  
  11. #define FUNCTION(dir,name) CONCAT2(dir,name)  
  12. #define TYPE(dir) dir  
  13. #define VIEW(dir,name) CONCAT2(dir,name)  
  14. #define QUALIFIED_TYPE(dir) TYPE(dir)  
  15. #define QUALIFIED_VIEW(dir,name) CONCAT2(dir,name)  
  16. #else  
  17. #define FUNCTION(a,c) CONCAT3(a,SHORT,c)  
  18. #define TYPE(dir) CONCAT2(dir,SHORT)  
  19. #define VIEW(dir,name) CONCAT3(dir,SHORT,name)  
  20. #define QUALIFIED_TYPE(dir) TYPE(dir)  
  21. #define QUALIFIED_VIEW(dir,name) CONCAT3(dir,SHORT,name)  
  22. #endif  
 

 

 

 

 

    在这里,FUNCTION(dir,name)宏被定义为CONCAT2(dir,name),而TYPE(dir)宏被定义为dir。

    这样,当我们重新回到init.c并进入view_source.c编译时,便顺理成章的得到了gsl_block_alloc等一系列double精度的函数。

    现在,编译器只剩下一点扫尾工作,即进入templates_off.h文件,关闭已有宏定义,为下一步float精度的编译做好准备。这里节选代码如下:

[cpp]  view plain copy
  1. 【templates_off.h】  
  2. #ifdef FUNCTION  
  3. #undef FUNCTION  
  4. #endif  
  5. #ifdef CONCAT4  
  6. #undef CONCAT4  
  7. #endif  
  8. #ifdef CONCAT4x  
  9. #undef CONCAT4x  
  10. #endif  
  11. #ifdef CONCAT3  
  12. #undef CONCAT3  
  13. #endif  
  14. #ifdef CONCAT3x  
  15. #undef CONCAT3x  
  16. #endif  
  17. #ifdef CONCAT2  
  18. #undef CONCAT2  
  19. #endif  
  20. #ifdef CONCAT2x  
  21. #undef CONCAT2x  
  22. #endif  
  23. #ifdef TYPE  
  24. #undef TYPE  
  25. #endif  
  26. ......  
  27. #undef BASE  
  28. #undef BASE_EPSILON  
  29. #undef SHORT  
  30. #undef ATOMIC  
  31. #undef MULTIPLICITY  
  32. #undef IN_FORMAT  
  33. #undef OUT_FORMAT  
  34. #undef ATOMIC_IO  
  35. #undef ZERO  
  36. #undef ONE  
  37. ......  
  38. #ifdef FP  
  39. #undef FP  
  40. #endif  
 

 

     这样double精度的函数编译工作便完成了。按照init.c中的流程,编译器将定义宏BASE_FLOAT,并开始float精度函数的编译。读者可以自己从上面的代码中体会其中的精妙。 


你可能感兴趣的:(gsl中的代码重用技术)