VC++编译GSL

 

1 VC++    1

1.1 修改行结束符    1

1.2 修改#include "*.c" #include "*.inl"    2

1.3 重命名重复的 *.c 文件    5

1.4 声明文件与实现文件分离    6

1.5 修改#include "*.h" 为相对路径    7

1.6 新建VC项目    8

1.6.1 目录结构    8

1.6.2 添加源文件    10

1.7 预编译头文件    11

1.7.1 创建文件    12

1.7.2 配置VC项目    12

1.7.3 修改*.c文件    14

1.7.4 修改*.inl文件    15

1.8 修改文件    16

1.8.1 gsl_types.h    16

1.8.2 gsl_inline.h    17

1.8.3 build.h    17

1.8.4 config.h    18

1.8.5 bspline\bspline.h    19

1.9 模块定义文件    19

1.10 编译GSL    23

1.10.1 接口变量    23

1.10.2 普通接口函数    23

1.10.3 准内联接口函数    24

1.11 客户程序调用    28

1.11.1 调用GSL动态库,启用内联    28

1.11.2 调用GSL动态库,禁用内联    29

1.11.3 调用GSL静态库,启用内联    29

1.11.4 调用GSL静态库,禁用内联    30

1.12 移植到Windows CE    31

1.12.1 关于DEBUG    31

1.12.2 关于errno.h    31

1.12.3 其它    32

 

 

1 VC++

本章将以gsl-1.16为例,说明如何使用VC++编译GSL。首先是解压gsl-1.16.tar.gz,这里假定解压至G:\gsl-1.16\src,其目录结构如下图所示:

1.1

1.1 修改行结束符

GSL源代码的行结束符是回车(0DH),在Windows下最好更换为回车、换行(0DH0AH)。当然,不修改行结束符并不影响GSL的编译。

Windows资源管理器里,进入G:\gsl-1.16\src搜索*.c*.h(图1.2所示),然后把这些文件拖放到程序vcHelper的"编码与换行"页面(图1.3所示)。vcHelper的下载方法:进入网盘 http://pan.baidu.com/s/1gd7XDkf 再进入 public\Tools\vcHelper 下载压缩包。

1.2

1.3

上图中的"G:\gsl-1.16\src\blas\blas.c""G:\gsl-1.16\src\blas\gsl_blas.h"……表示这些文件被改写。

1.2 修改#include "*.c" #include "*.inl"

GSL里有一些*.c文件比较特殊:编译器并不直接编译它,而是某些c文件(宿主文件)嵌入了其内容,当编译宿主文件时这类文件才发生作用。

举例说明,G:\gsl-1.16\block\block.c的部分内容如下:

#define BASE_DOUBLE

#include "templates_on.h"

#include "block_source.c"

#include "templates_off.h"

#undef BASE_DOUBLE

 

#define BASE_FLOAT

#include "templates_on.h"

#include "block_source.c"

#include "templates_off.h"

#undef BASE_FLOAT

可以看到:block_source.c被多次嵌入到block.c里。编译器并不需要直接编译block_source.c,只是在编译block.cblock_source.c才发挥作用。

GSL这么做的目的是为了节省人工编写代码的工作量,上述代码的用意为:针对double编译一次block_source.c,再针对float编译一次block_source.c……这个非常类似于C++的模板。

现在的问题是:对于一个并不想深入研究GSL代码的程序员而言,他根本无法区分block_source.cblock.c:同样的*.c文件,如何确定哪个文件应该编译,哪个文件不应该编译?解决方法就是重命名:把#include "block_source.c"修改为#include "block_source.inl",同时重命名block_source.cblock_source.inl

运行VS2008,打开"查找和替换"对话框,并按下图进行配置,然后单击"查找全部"按钮。

1.4

查找结果如下表所示。

查找全部 "include*.c", 大小写匹配, 通配符, 子文件夹, ……

G:\gsl-1.16\src\block\block.c(7):#include "block_source.c"

G:\gsl-1.16\src\block\block.c(13):#include "block_source.c"

…… …… ……

…… …… ……

G:\gsl-1.16\src\vector\view.c(174):#include "view_source.c"

匹配行: 956 匹配文件: 161 合计搜索文件: 1344

通过上表可知共有956个匹配项,手动修改的话工作量会比较大,而且容易出错。因此,可以通过编写程序来实现修改,具体而言就是:替换G:\gsl-1.16\src\block\block.c里的第7行代码#include "block_source.c"#include "block_source.inl",同时重命名G:\gsl-1.16\src\block\block_source.cblock_source.inl。上表中的956项需要逐一进行修改。

运行vcHelper程序,然后把上表内容复制到"#include"*.c""页面的文本框内,最后单击"应用"按钮,完成相应的更改工作。如下图所示:

1.5

1.3 重命名重复的 *.c 文件

GSL的源代码文件(*.c)存在同名的现象,如下图所示:在G:\gsl-1.16\src目录查找*.c,将会发现有多个inline.c文件。

1.6

VC++编译时不允许源代码文件重名,因此需要将这些重名的文件改名。手工改名工作量较大,且容易出错,可以编写程序去完成此项工作。

vcHelper程序已经具有此项功能,具体操作为:将上图所有的*.c文件拖放至下图所示的主界面即可。

1.7

上图的G:\gsl-1.16\src\cdf\beta.c==>beta_cdf.c表示将G:\gsl-1.16\src\cdf\beta.c更名为beta_cdf.c

在图1.6中的几个inline.c文件被重命名为如下文件:

inline_combination.c

inline_complex.c

inline_interpolation.c

inline_multiset.c

inline_permutation.c

inline_qrng.c

inline_rng.c

inline_combination.c表示它在combination目录,原名为inline.c。如果inline_combination.c这个名称也已经存在,则会重命名inline.cinline_combination#.c#号是从1开始编号的整数)。

1.4 声明文件与实现文件分离

GSL接口函数的声明文件为gsl_*.h,它们与实现代码(*.c)混在一起。本节将把声明文件和实现文件分隔开来,具体操作如下:

1、在G:\gsl-1.16目录下新建inc目录;

2、搜索G:\gsl-1.16\src目录下的gsl_*.h,把这些文件移至G:\gsl-1.16\inc

3、在G:\gsl-1.16\src目录下复制、粘贴config.h.in文件,然后重命名为config.h。可以使用vcHelper程序修改config.h的行结束符。

1.5 修改#include "*.h" 为相对路径

声明文件与实现文件分离后,以前的代码#include <gsl/gsl_math.h>可能就无法正常编译了,为此需要修改#include <gsl/gsl_math.h>#include "../../inc/gsl_math.h",即包含文件时采用相对路径。

使用"VC编程助手"程序完成此项工作。程序运行界面如下图所示:

1.8

上图中,首先单击"扫描"按钮。扫描后会显示"扫描1345/1752",它表示G:\gsl-1.16里共有1752个文件。其中1345个文件的扩展名为(.c;.cc;.cpp;.cxx;.h;.hh;.hpp;.hxx;.inc;.inl),这些文件就是准备修改的文件。单击"自动修改"按钮即可完成#include语句的修改。

下面是src\complex\math.c文件在执行"VC编程助手"程序前后的变化。

#include <gsl/gsl_math.h>

#include <gsl/gsl_complex.h>

#include <gsl/gsl_complex_math.h>

#include "../../inc/gsl_math.h"

#include "../../inc/gsl_complex.h"

#include "../../inc/gsl_complex_math.h"

1.6 新建VC项目

1.6.1 目录结构

新建四个VC项目。其目录结构如下图所示:

1.9

make-dll        用于生成GSL动态库

make-libS    用于生成GSL静态库(C运行时库为单线程)

make-libT    用于生成GSL静态库(C运行时库为多线程)

make-libD    用于生成GSL静态库(C运行时库为多线程DLL

每个make-???目录下又有若干子目录,如下图所示。make-dll目录下有vc6vc2002vc2003……它们分别表示使用VC++6.0VC++.NETVC++2003……编译GSL

1.10

编译生成的成果文件存放在图1.9所示的bin目录下,如下图所示。vc6-win32-RA表示编译器是vc6,平台是win32RA表示Release Ansi版本。vc2008-Pocket PC 2003 (ARMV4)-RU表示编译器是vc2008,平台是Pocket PC 2003 (ARMV4)RU表示Release Unicode版本。

1.11

bin\vc6-win32-RA目录下的文件如下图所示:

GSL.dllGSL.lib        动态库及其导入库

GSLS.lib                静态库(C运行时库为单线程)

GSLT.lib                静态库(C运行时库为多线程)

GSLD.lib                静态库(C运行时库为多线程DLL

1.12

1.6.2 添加源文件

G:\gsl-1.16目录下一千多个文件(*.c;*.h;*.inl)添加到VC项目里,这是一项艰巨的工程。这里使用"VC 编程助手"来添加,如下图所示:

1.13

"生成文件树"里的文本框输入G:\gsl-1.16\src,然后单击"生成文件树"按钮,即可生成文件树。上图可以看到:生成的文件树里有1126个文件。

拖动G:\gsl-1.16\make-dllG:\gsl-1.16\make-libSG:\gsl-1.16\make-libTG:\gsl-1.16\make-libD目录到"替换文件树"里的文本框,则"VC 编程助手"会自动找到这些目录里的VC项目文件(*.dsp;*.vcproj;*.vcxproj)。单击"替换文件树"按钮,则"VC 编程助手"会把VC项目文件里的文件树替换为生成的文件树。如下图所示:

1.14

注意:有些*.c文件是测试代码(内含main函数),这些文件需要从VC项目里移除。具体如下:

1test*.c需要移除;

2doc目录需要移除;

3gsl-histogram.cgsl-randist.c需要移除。

1.7 预编译头文件

config.h文件非常重要,它里面有一些非常重要的宏。*.c文件为了获得这些宏定义,几乎都包含了config.h

使用预编译头文件的目的有两个:一是确保所有的*.c文件都能包含config.h。事实上complex\inline.ccombination\inline.cmultiset\inline.c因为没有包含config.h,编译时就会有些问题;二是提高编译的速度。

1.7.1 创建文件

G:\gsl-1.16\src下新建文件StdAfx.c,其内容如下:

#include "StdAfx.h"

G:\gsl-1.16\src下新建文件StdAfx.h,其内容如下:

#pragma once

 

#define _CRT_NON_CONFORMING_SWPRINTFS

#define _CRT_SECURE_NO_WARNINGS

 

#if defined(_LIB) //编译生成静态库

#define GSLLib

#else //编译生成动态库

#define GSLLib __declspec(dllexport)

#endif

 

//是否使用内联函数

#define USE_INLINE 1

 

#include <MATH.H>

#include <STDLIB.H>

#include "config.h"

1.7.2 配置VC项目

把这两个文件增加到VC项目里,然后配置项目属性:

1.15

接着配置StdAfx.c的属性:

1.16

也就是说:StdAfx.c用来生成预编译头文件(*.pch),其它的*.c文件使用这个预编译头文件。

1.7.3 修改*.c文件

现在替换*.c文件里的#include*config.h#include "StdAfx.h。使用VS2008的"在文件中替换"功能,界面如下:

1.17

上图中,单击"全部替换"按钮,完成替换工作。

还需要手工完成如下工作:

1complex\inline.ccombination\inline.cmultiset\inline.cutils\placeholder.cG:\gsl-1.16\src\cblas目录下的*.c,需要将#include "StdAfx.h"增加至第一行;

2G:\gsl-1.16\src\cdf\binomial_cdf.c里有两个#include "StdAfx.h",请删除第二个。

1.7.4 修改*.inl文件

现在替换*.inl文件里的#include*config.h"为空。使用VS2008的"在文件中替换"功能。

1.18

1.8 修改文件

1.8.1 gsl_types.h

修改inc\gsl_types.h,使其内容如下

#ifndef __GSL_TYPES_H__

#define __GSL_TYPES_H__

#ifndef GSL_VAR

#define GSL_VAR extern GSLLib

#endif

#endif

1.8.2 gsl_inline.h

修改inc\gsl_inline.h,使其内容如下

#ifndef __GSL_INLINE_H__

#define __GSL_INLINE_H__

#ifndef COMPILE_INLINE_STATIC

#ifdef HAVE_INLINE //启用内联

#if defined(USE_INLINE) && defined(_LIB)

//编译生成 GSL 静态库

#define INLINE_DECL static inline

#define INLINE_FUN static inline

#else

#define INLINE_DECL GSLLib inline

#define INLINE_FUN GSLLib inline

#endif

#else //禁用内联

#define INLINE_DECL GSLLib

#define INLINE_FUN GSLLib

#endif

#define GSL_RANGE_COND(x) (x)

#endif

#endif /* __GSL_INLINE_H__ */

1.8.3 build.h

修改src\build.h,使其内容如下

#pragma once

#undef INLINE_DECL

#undef INLINE_FUN

#ifdef COMPILE_INLINE_STATIC

#undef    __GSL_INLINE_H__

#define    __GSL_INLINE_H__    //防止再次包含gsl_inline.h

#undef    GSL_RANGE_CHECK

#define    GSL_RANGE_CHECK 1

#undef    GSL_RANGE_COND

#define    GSL_RANGE_COND(x) (gsl_check_range && (x))

#ifdef HAVE_INLINE //使用内联

#if defined(USE_INLINE) && defined(_LIB)

//编译生成 GSL 静态库

#define INLINE_DECL

#define INLINE_FUN

#else

#define INLINE_DECL        GSLLib inline

#define INLINE_FUN        GSLLib inline

#endif

#else //不使用内联

#define INLINE_DECL        GSLLib

#define INLINE_FUN        GSLLib

#define HAVE_INLINE

#endif

#endif

1.8.4 config.h

请修改src\config.h

1、删除下面四段代码

#undef HAVE_C99_INLINE

#undef HAVE_INLINE

#undef HIDE_INLINE_STATIC

#ifndef __cplusplus

#undef inline

#endif

2、将下面一段代码增加到config.h的最顶端

#pragma once

 

#if USE_INLINE //启用内联

#define HAVE_C99_INLINE

#define HAVE_INLINE

#define HIDE_INLINE_STATIC

#ifndef __cplusplus

#define inline __inline

#endif

#else //禁用内联

#undef HAVE_C99_INLINE

#undef HAVE_INLINE

#undef HIDE_INLINE_STATIC

#ifndef __cplusplus

#define inline

#endif

#endif

3、修改代码

修改前

修改后

#undef HAVE_DECL_FREXP

#define HAVE_DECL_FREXP 1

#undef HAVE_DECL_HYPOT

#define HAVE_DECL_HYPOT 1

#undef HAVE_DECL_LDEXP

#define HAVE_DECL_LDEXP 1

不修改这些代码,则编译时会出错。如:#undef HAVE_DECL_HYPOT时,编译sys\fcmp.c就会出错。

fcmp.c的主要代码如下:

#include "../config.h"

#include "../../../inc/gsl_sys.h"

#include <math.h>

config.h里,有如下代码。此时,hypot被映射为gsl_hypot

#if !HAVE_DECL_HYPOT

#define hypot gsl_hypot

#endif

接下来会处理math.h里的如下代码

_CRTIMP double __cdecl hypot(double, double);

因为hypot是一个宏,结果就变成了如下代码:

_CRTIMP double __cdecl gsl_hypot(double, double);

上面是函数gsl_hypot的声明,它与gsl_sys.h里的gsl_hypot函数声明(下表所示)冲突。导致程序编译失败。

double gsl_hypot (const double x, const double y);

1.8.5 bspline\bspline.h

#pragma once增加为G:\gsl-1.16\src\bspline\bspline.h的第一行。否则编译时会出错。

1.9 模块定义文件

生成GSL动态库时需要导出一些变量和函数,为此需要模块定义文件。请在G:\gsl-1.16\src目录新建文件GSL.def。然后输入如下内容

EXPORTS

gsl_check_range DATA

gsl_interp_akima DATA

……

GSL_MAX_DBL

GSL_MAX_INT

GSL_MIN_DBL

GSL_MIN_INT

cblas_caxpy

cblas_ccopy

……

说明:

1gsl_check_rangegsl_interp_akima……是导出的变量;

2GSL_*cblas_*gsl_*这些都是导出函数。

v16.0GSL145个导出变量,4199个导出函数。如何快速获取这些变量和函数的名称?

运行vc2010,新建一个项目,把gsl_*.h添加到该项目。在Class View下,可以查看"Global Functions and Variables",如下图所示。可以把这些函数和变量复制出来。

1.19

复制出来的名称以空格分隔,可以使用Word把空格替换为" DATA^p"(导出变量,^p表示回车换行)或"^p"(导出函数,^p表示回车换行)。注意:请禁用Word的行首字母大写功能。

下图是在Word 2003 里,替换所有的空格为" DATA^p"

1.20

替换结果如下图所示:

1.21

这样的内容,是可以直接复制粘贴到def文件里的。

注意:导出的名称可能会有重复的。可使用"VC编程助手"的"排序合并"功能进行排序并剔除重复项,如下图所示:

1.22

使用VC++6.0编译生成的GSL.dll,其导出符号如下图所示:

1.23

类似"??_C@_0DA@IJFJ@g?3?2?"这样的符号,是什么呢?这些是内联函数里的字符串。下面的代码可以获得??_C@_0DA代表的字符串:

HMODULE    hMod    =    GetModuleHandle(_T("GSL"));

FARPROC    pfn    =    GetProcAddress(hMod,"??_C@_0DA");

const char*    s    =    (const char*)pfn;

使用vc2008编译生成GSL动态库时,不再导出这些内联函数里的字符串。

1.10 编译GSL

GSL的接口有两类:接口变量和接口函数。

gsl_version就是一个接口变量,客户端程序可以直接使用此变量。

接口函数分为两类,一类是普通的接口函数,另一类是准内联接口函数。什么是准内联函数呢?就是它可以内联也可以不内联。

1.10.1 接口变量

接口变量的声明一般在头文件里,如:gsl_version的声明就在gsl_version.h

GSL_VAR const char * gsl_version;

接口变量的初始化一般在*.c文件里,如:gsl_version的初始化就在gsl_version.c

const char * gsl_version = GSL_VERSION;

这里要注意宏GSL_VAR。在gsl_types.h里,它被定义为extern GSLLib

当生成GSL动态库时,GSLLib__declspec(dllexport),此时gsl_version的声明就是:

extern __declspec(dllexport) const char * gsl_version;

__declspec(dllexport)把变量gsl_version给导出了。还有一种办法导出接口变量,那就是在模块定义文件(*.DEF)里增加如下语句:

gsl_version DATA

当生成GSL静态库时,GSLLib是空,此时gsl_version的声明就是:

extern const char * gsl_version;

也就是说生成GSL静态库时,接口变量就是全局变量。

客户端程序连接GSL动态库时,接口变量的声明如下:

extern __declspec(dllimport) const char * gsl_version;

客户端程序连接GSL静态库时,接口变量的声明如下:

extern const char * gsl_version;

1.10.2 普通接口函数

普通接口函数的声明一般在头文件里,如:函数gsl_complex_polar的声明就在gsl_complex_math.h里。

gsl_complex gsl_complex_polar(double r, double theta);

普通接口函数的实现一般在*.c文件里,如:complex\math.c 里有函数gsl_complex_polar的实现代码:

gsl_complex gsl_complex_polar (double r, double theta)

{/* return z = r exp(i theta) */

gsl_complex z;

GSL_SET_COMPLEX (&z, r * cos (theta), r * sin (theta));

return z;

}

当生成GSL动态库时,在模块定义文件(GSL.def)里增加如下语句即可导出gsl_complex_polar函数。

gsl_complex_polar

其实更好的做法是修改gsl_complex_polar的声明、实现代码如下

GSLLib gsl_complex gsl_complex_polar(double r, double theta);

GSLLib gsl_complex gsl_complex_polar (double r, double theta)

{/* return z = r exp(i theta) */

gsl_complex z;

GSL_SET_COMPLEX (&z, r * cos (theta), r * sin (theta));

return z;

}

当生成GSL动态库时,GSLLib__declspec(dllexport)。当生成GSL静态库时,GSLLib是空。当客户程序连接GSL动态库时,GSLLib__declspec(dllimport)(这点尤为重要,根据MSDN文档介绍这样会提高调用导出函数的效率)。当客户程序连接GSL静态库时,GSLLib是空。

1.10.3 准内联接口函数

准内联接口函数的声明及内联代码一般在头文件里,如:gsl_complex_math.h里有gsl_complex_rect的声明和内联代码,如下所示。

INLINE_DECL gsl_complex gsl_complex_rect (double x, double y);

#ifdef HAVE_INLINE

INLINE_FUN gsl_complex gsl_complex_rect (double x, double y)

{/* return z = x + i y */

gsl_complex z;

GSL_SET_COMPLEX (&z, x, y);

return z;

}

#endif

准内联接口函数的外部代码一般在*.c文件里,如:complex\inline.c的文件内容:

#define COMPILE_INLINE_STATIC

#include "../build.h"

#include "../../inc/gsl_complex_math.h"

通过COMPILE_INLINE_STATICbuild.h,可把gsl_complex_math.h里的内联代码变成外部代码。

1.10.3.1 启用内联

StdAfx.h里,修改宏USE_INLINE的定义即可控制准内联函数是否内联:

#define USE_INLINE 1

USE_INLINE定义为1,那么宏 HAVE_INLINE 将被定义,如下所示(config.h里的代码):

#if USE_INLINE //启用内联

#define HAVE_C99_INLINE

#define HAVE_INLINE

#define HIDE_INLINE_STATIC

#ifndef __cplusplus

#define inline __inline

#endif

#else //禁用内联

#undef HAVE_C99_INLINE

#undef HAVE_INLINE

#undef HIDE_INLINE_STATIC

#ifndef __cplusplus

#define inline

#endif

#endif

接下来查看gsl_inline.h里的宏定义

#ifdef HAVE_INLINE //启用内联

#if defined(USE_INLINE) && defined(_LIB)

//编译生成 GSL 静态库

#define INLINE_DECL        static inline

#define INLINE_FUN        static inline

#else

#define INLINE_DECL        GSLLib inline

#define INLINE_FUN        GSLLib inline

#endif

#else //禁用内联

有了INLINE_DECLINLINE_FUN的定义,就可展开gsl_complex_math.hgsl_complex_rect的声明和内联代码。

一、编译生成GSL静态库

gsl_complex_math.hgsl_complex_rect的声明和内联代码如下:

static __inline gsl_complex gsl_complex_rect(double x, double y);

static __inline gsl_complex gsl_complex_rect(double x, double y)

{

gsl_complex z;

GSL_SET_COMPLEX (&z, x, y);

return z;

}

complex\inline.c里gsl_complex_rect的声明和实现代码又是什么情况呢?下面是complex\inline.c的文件内容:

#define COMPILE_INLINE_STATIC

#include "../build.h"

#include "../../inc/gsl_complex_math.h"

关键在于build.h,其重要代码如下:

#ifdef HAVE_INLINE //使用内联

#if defined(USE_INLINE) && defined(_LIB)

//编译生成 GSL 静态库

#define INLINE_DECL

#define INLINE_FUN

#else

#define INLINE_DECL GSLLib inline

#define INLINE_FUN GSLLib inline

#endif

#else //不使用内联

也就是说在complex\inline.c里,gsl_complex_rect的声明、实现代码如下。此时的gsl_complex_rect是一个外部函数。

gsl_complex gsl_complex_rect(double x, double y);

gsl_complex gsl_complex_rect(double x, double y)

{

gsl_complex z;

GSL_SET_COMPLEX (&z, x, y);

return z;

}

编译GSL时,需要分两种情况讨论:

1、内联功能被启用时,如:编译ReleaseGSL静态库。此时gsl_complex_rect有两个版本:外部版本和内联版本。外部版本就是complex\inline.c里的版本;内联版本就是被编译器展开的版本。

2、内联功能被禁用时,如:编译DebugGSL静态库。此时gsl_complex_rect也有两个版本:外部版本和外联版本。外部版本就是complex\inline.c里的版本;外联版本就是gsl_complex_rect函数没有被内联展开,而是编译成了函数。为防止外联版本与外部版本名称冲突,在__inline前增加了static关键字。

也就是说,不论gsl_complex_rect是否内联,GSL静态库中一定会有gsl_complex_rect的外部版本。这是为客户程序调用而准备的。

二、编译生成GSL动态库或客户程序调用GSL库时

gsl_complex_math.hgsl_complex_rect的声明和内联代码如下。注意:complex\inline.c里也有一份同样的代码。

GSLLib __inline gsl_complex gsl_complex_rect(double x, double y);

GSLLib __inline gsl_complex gsl_complex_rect(double x, double y)

{

gsl_complex z;

GSL_SET_COMPLEX (&z, x, y);

return z;

}

注意上面的GSLLib

1、当编译GSL生成动态库时,GSLLib__declspec(dllexport)

2、当客户程序调用GSL动态库时,GSLLib__declspec(dllimport)

3、当客户程序调用GSL静态库时,GSLLib为空。此时,以Release版编译客户程序(内联被启用),客户程序对gsl_complex_rect的调用将使用内联版本;以Debug版编译客户程序(内联被禁用),客户程序对gsl_complex_rect的调用将使用外联版本,这时连接的其实是GSL静态库里的外部版本。

1.10.3.2 禁用内联

USE_INLINE定义为0,那么宏 HAVE_INLINE 就不会被定义。

gsl_complex_math.hgsl_complex_rect的声明代码如下:

GSLLib gsl_complex gsl_complex_rect(double x, double y);

gsl_complex_math.hgsl_complex_rect的内联代码被禁用了。

complex\inline.c里,gsl_complex_rect的声明、实现代码如下:

GSLLib gsl_complex gsl_complex_rect(double x, double y);

GSLLib gsl_complex gsl_complex_rect(double x, double y)

{

gsl_complex z;

GSL_SET_COMPLEX (&z, x, y);

return z;

}

也就是说:禁用内联后,准内联接口函数就变成了普通接口函数了。

还有一点需要说明:#include "../build.h"之后再#include "../../inc/gsl_complex_math.h",则gsl_complex_math.h里的#include "gsl_inline.h"将失效。因为build.h里定义了#define __GSL_INLINE_H__,防止再次包含gsl_inline.h,破坏INLINE_DECLINLINE_FUN的定义。其实更好的作法是在gsl_inline.h里增加对宏COMPILE_INLINE_STATIC是否定义的判断,如下所示:

#ifndef COMPILE_INLINE_STATIC

#ifdef HAVE_INLINE //启用内联

#if defined(USE_INLINE) && defined(_LIB)

//编译生成 GSL 静态库

#define INLINE_DECL static inline

#define INLINE_FUN static inline

#else

#define INLINE_DECL GSLLib inline

#define INLINE_FUN GSLLib inline

#endif

#else //禁用内联

#define INLINE_DECL GSLLib

#define INLINE_FUN GSLLib

#endif

#define GSL_RANGE_COND(x) (x)

#endif

1.11 客户程序调用

为方便客户程序调用GSL库,请在G:\gsl-1.16\inc下创建如下文件:_Inc.h_Inc.inl_IncInline.h_IncInline.inl_Static.h_Static.inl_StaticInline.h_StaticInline.inlinc.h

1.11.1 调用GSL动态库,启用内联

客户程序应该包含如下两个文件:

#include "G:/gsl-1.16/inc/_IncInline.h"

#include "G:/gsl-1.16/inc/_IncInline.inl"

_IncInline.h文件内容如下:

#pragma once

#define GSLLib __declspec(dllimport)

 

#define HAVE_C99_INLINE

#define HAVE_INLINE

#define HIDE_INLINE_STATIC

#ifndef __cplusplus

#define inline __inline

#endif

 

#include "inc.h"

 

#undef HAVE_C99_INLINE

#undef HAVE_INLINE

#undef HIDE_INLINE_STATIC

#ifndef __cplusplus

#undef inline

#endif

inc.h文件内容如下(其实就是包含所有的gsl_*.h):

#pragma once

#include "gsl_blas.h"

... ... ...

#include "gsl_wavelet2d.h"

_IncInline.inl文件内容如下,其实质就是自动连接GSL.lib。注意:编译客户端程序时必须设置GSL.lib的目录。

#pragma comment(lib,"GSL.lib")

1.11.2 调用GSL动态库,禁用内联

客户程序应该包含如下两个文件:

#include "G:/gsl-1.16/inc/_Inc.h"

#include "G:/gsl-1.16/inc/_Inc.inl"

_Inc.h文件内容如下:

#pragma once

#define GSLLib __declspec(dllimport)

#include "inc.h"

_Inc.inl文件内容如下:

#pragma comment(lib,"GSL.lib")

1.11.3 调用GSL静态库,启用内联

客户程序应该包含如下两个文件:

#include "G:/gsl-1.16/inc/_StaticInline.h"

#include "G:/gsl-1.16/inc/_StaticInline.inl"

_StaticInline.h文件内容如下:

#pragma once

#define GSLLib

 

#define HAVE_C99_INLINE

#define HAVE_INLINE

#define HIDE_INLINE_STATIC

#ifndef __cplusplus

#define inline __inline

#endif

 

#include "inc.h"

 

#undef HAVE_C99_INLINE

#undef HAVE_INLINE

#undef HIDE_INLINE_STATIC

#ifndef __cplusplus

#undef inline

#endif

#include "inc.h"之前,启用了内联功能,在#include "inc.h"之后取消了inline的宏定义。

_StaticInline.inl文件内容如下,其实质就是根据客户端程序使用的C函数库,自动选择相应的GSL?.lib。注意:编译客户端程序时必须设置GSL?.lib的目录。

#ifdef _MT

#ifdef _DLL //使用多线程 DLL 版的 C 函数库

#pragma comment(lib,"GSLD.lib")

#else //使用多线程版的 C 函数库

#pragma comment(lib,"GSLT.lib")

#endif

#else //使用单线程版的 C 函数库

#pragma comment(lib,"GSLS.lib")

#endif

1.11.4 调用GSL静态库,禁用内联

客户程序应该包含如下两个文件:

#include "G:/gsl-1.16/inc/_Static.h"

#include "G:/gsl-1.16/inc/_Static.inl"

_Static.h文件内容如下:

#pragma once

#define GSLLib

#include "inc.h"

_Static.inl文件内容与_StaticInline.inl完全相同。

1.12 移植到Windows CE

1.12.1 关于DEBUG

编译PocketPC 2003 Debug 版代码时,将会出现编译错误。原因就是宏DEBUG被定义了。

ode-initval2\msbdf.c为例进行说明。msbdf_update函数里有如下代码:

if (*nJ == 0 || *nJ > MSBDF_JAC_WAIT ||

(t == failt && (gammarel < c || hratio < 1.0)))

{

#ifdef DEBUG

printf ("-- evaluate jacobian\n");

#endif

int s = GSL_ODEIV_JA_EVAL (sys, t, y, dfdy->data, dfdt);

因为定义了宏DEBUG,因此变量 s 在初始化前的代码 printf("……")被启用。在VC++C编译器里,变量的声明必须在执行语句之前。此时,就会出现编译错误。解决方法有两个:一是改代码;二是编译生成Release版。

VC++编译Win32 DebugGSL时,将定义宏_DEBUG,而不是定义宏DEBUG,因此VC++可以正常编译Win32 DebugGSL

1.12.2 关于errno.h

Windows CEC函数集合不是很完备,缺少一些C函数及头文件,如:缺少errno.h为此需要修改gsl_errno.hntuple\ntuple.c中的#include <errno.h>,修改后的代码如下:

#ifndef _WIN32_WCE

#include <errno.h>

#endif

也就是编译生成Windows CE代码时,将不再包含errno.h

1.12.3 其它

增加如下代码至config.h的最后

#ifdef _WIN32_WCE

#define hypot _hypot

__inline void abort(void)

{

}

__inline char*getenv(const char *varname)

{

return varname;

}

#endif

说明:

1Windows CE平台没有hypot函数,相应的函数是_hypot

2Windows CE平台未实现函数abortgetenv,上述代码只能保证编译通过。

posted @ 2016-12-13 10:16  hanford  阅读(566)  评论(0编辑  收藏  举报