目录

第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下最好更换为回车、换行(0DH、0AH)。当然,不修改行结束符并不影响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.c时block_source.c才发挥作用。

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

现在的问题是:对于一个并不想深入研究GSL代码的程序员而言,他根本无法区分block_source.c和block.c:同样的*.c文件,如何确定哪个文件应该编译,哪个文件不应该编译?解决方法就是重命名:把#include "block_source.c"修改为#include "block_source.inl",同时重命名block_source.c为block_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.c为block_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.c为inline_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目录下有vc6、vc2002、vc2003……它们分别表示使用VC++6.0、VC++.NET、VC++2003……编译GSL。

图1.10

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

图1.11

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

GSL.dll、GSL.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-dll、G:\gsl-1.16\make-libS、G:\gsl-1.16\make-libT、G:\gsl-1.16\make-libD目录到"替换文件树"里的文本框,则"VC 编程助手"会自动找到这些目录里的VC项目文件(*.dsp;*.vcproj;*.vcxproj)。单击"替换文件树"按钮,则"VC 编程助手"会把VC项目文件里的文件树替换为生成的文件树。如下图所示:

图1.14

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

1、test*.c需要移除;

2、doc目录需要移除;

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

1.7 预编译头文件

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

使用预编译头文件的目的有两个:一是确保所有的*.c文件都能包含config.h。事实上complex\inline.c、combination\inline.c、multiset\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

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

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

1、complex\inline.c、combination\inline.c、multiset\inline.c、utils\placeholder.c,G:\gsl-1.16\src\cblas目录下的*.c,需要将#include "StdAfx.h"增加至第一行;

2、G:\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

……

说明:

1、gsl_check_range、gsl_interp_akima……是导出的变量;

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

v16.0的GSL有145个导出变量,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_STATIC和build.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_DECL和INLINE_FUN的定义,就可展开gsl_complex_math.h里gsl_complex_rect的声明和内联代码。

一、编译生成GSL静态库

gsl_complex_math.h里gsl_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、内联功能被启用时,如:编译Release版GSL静态库。此时gsl_complex_rect有两个版本:外部版本和内联版本。外部版本就是complex\inline.c里的版本;内联版本就是被编译器展开的版本。

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

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

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

gsl_complex_math.h里gsl_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.h里gsl_complex_rect的声明代码如下:

GSLLib gsl_complex gsl_complex_rect(double x, double y);

gsl_complex_math.h里gsl_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_DECL和INLINE_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.inl、inc.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 Debug版GSL时,将定义宏_DEBUG,而不是定义宏DEBUG,因此VC++可以正常编译Win32 Debug版GSL。

1.12.2 关于errno.h

Windows CE的C函数集合不是很完备,缺少一些C函数及头文件,如:缺少errno.h为此需要修改gsl_errno.h和ntuple\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

说明:

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

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

VC++编译GSL的更多相关文章

  1. 用VC编译lua源码,生成lua语言的解释器和编译器

    用VC编译lua源码,生成lua语言的解释器和编译器 1.去网址下载源码 http://www.lua.org/download.html 2.装一个VC++,我用的是VC6.0 3.接下来我们开始编 ...

  2. VC++编译MPIR 2.7.0

    目录 第1章编译    2 1.1 简介    2 1.2 下载    3 1.3 解决方案    4 1.4 创建项目    5 1.5 复制文件树    6 1.6 不使用预编译头文件    8 ...

  3. 让VC编译出来的程序不依赖于msvcr80.dll/msvcr90.dll/msvcr100.dll等文件

    ---转载:http://hi.baidu.com/liu_haitao/item/e2157ac3a3c32a0bc610b253 让VC编译出来的程序不依赖于msvcr80.dll/msvcr90 ...

  4. 【Python学习】由于windows环境问题导致的不能安装某些需要VC编译的插件

    由于windows环境问题导致的不能安装某些需要VC编译的插件 下载地址:http://www.lfd.uci.edu/~gohlke/pythonlibs/ 安装方法: 在CMD中输入 pip in ...

  5. VC编译连接选项详解

    VC编译连接选项详解 大家可能一直在用VC开发软件,但是对于这个编译器却未必很了解.原因是多方面的.大多数情况下,我们只停留在“使用”它,而不会想去“了解”它.因为它只是一个工具,我们宁可把更多的精力 ...

  6. 迁移到MSYS2 与 Qt 工具链注意的几个事情(g++在链接时,符号依赖项查找遵循从左至右的顺序,但qmake会自动合并造成错误。使用脚本给Mingw32-make创造出一个局部的VC编译环境)

    Microsoft Visual Studio 2015社区版提供了强大的开发体验,且 Qt 提供了预编译版本.然而,由于客户提出兼容Windows XP ~ Windows 8.1 这样宽泛的环境要 ...

  7. VC 编译 MATLAB 的 mex 文件

    VC 编译 MATLAB 的 mex 文件mex 文件是 MATLAB 调用其他程序设计语言程序或算法的接口.在 Windows 环境中,mex 文件是扩展文件名为 DLL 的动态链接库,可以在 m ...

  8. vc编译 curl 7.36.0

    CURL邮件列表中提到官方最新版本的windows devel包中缺少文件,而我又用不到https,所以我就自己下载源码包来编译了 下载源码包:http://curl.haxx.se/download ...

  9. vc编译 zlib 1.2.8

    最近用到gzip关的算法,于是想起了zlib这个库,于是将其下载下来编译. 首先,在官网上下载源码包:http://zlib.net/zlib-1.2.8.tar.gz 解压之后,打开vc 命令工具: ...

随机推荐

  1. Android中scrollview嵌套HorizontalScrollView卡顿现象解决

    开发中经验会遇到滑动里面嵌入滑动的问题,但是这种情况下触摸事件就会发生冲突.导致滑动非常卡,甚至出现程序停止响应.这种情况下我们一般需要重写view.下面给出重新scrollview的方法 publi ...

  2. WebForm跨页面传值---内置对象

    一.Response Response - 响应请求对象 string path = "Default2.aspx": (1)Response.Redirect(path); -- ...

  3. xcodeheader search 配置

    一般我们在xcode里面配置包含工程目录下头文件的时候,都要关联着相对路径和绝对路径,如果只是自己用这个项目,用绝对路径的问题不大,但是如果你把工程发给别人,别人就要在改这个绝对路径,这时候绝对路径的 ...

  4. #使用while循环输入1 2 3 4 5 6 8 9 10

    #!/usr/bin/env python #使用while循环输入1 2 3 4 5 6 8 9 10 import time start = 1 while True: if start == 7 ...

  5. Java程序员必备的6款最佳开发工具

    工欲善其事,必先利其器.每一个Java程序员都有其惯用的工具组件.对于Java程序员,各种有用的软件和工具泛滥成灾.初级开发人员要么找不到合适的工具,要么在寻找过程中浪费了大量的时间.下面,我将为大家 ...

  6. bzoj 3234: [Ahoi2013]立方体

    题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3234 题意:求长方体交的表面积. 思路:flood-fill const int ...

  7. iOS深入学习(再谈block)

    之前写过一篇博客,把Block跟delegate类比,说明了使用block,可以通过更少的代码实现代理的功能.那篇博客将block定义为类的property. 过了这么长时间,对于block的内容有了 ...

  8. python_way,day4 内置函数(callable,chr,随机验证码,ord),装饰器

    python_way,day4 1.内置函数 - 下 制作一个随机验证码 2.装饰器 1.内置函数 - 下 callable() #对象能否被调用 chr() #10进制数字对应的ascii码表中的内 ...

  9. CSS笔记(八)表格

    参考:http://www.w3school.com.cn/css/css_table.asp 实例: <html> <head> <style type="t ...

  10. Memcached通用类(基于Memcached Client Library)

    分享下自己编写的Memcached通用类.欢迎大家帮忙指点下哈~ 使用的是.NET memcached client library 客户端+Memcached Providers using Sys ...