Visual Stdio 环境下使用 GSL (GNU Scientific Library)
经測试。这里的方法不适用于VS2015。
* 这篇文章有点过时了。建议从以下网址下载能够在 vs 环境下编译的 GSL:*
https://github.com/BrianGladman/gsl
GNU Scientific Library (GSL)是一个开源的科学计算的函数库,功能非常强大。
网上介绍它的文章非常多,并且 GSL 的文档也写的非常的好,属于那种特别easy上手的函数库。
这里就不多对 GSL 进行介绍了。
今天要讲的是怎样在 Visual stdio 环境下使用这个库。事实上这方面的内容网上也有一些。只是採用的方法大多不太好。
有的是直接下载 GSL for Widows 来使用,可是这个 GSL for Widows 是 2006 年的GSL 1.8 ,古董级的版本号了,这个版本号缺少的功能太多。
还有一种方法是改造 MinGW 编译的生成的 dll。
事实上不同的 C 语言编译器编译生成的 dll 仅仅要遵守同样的 C calling Conven,原则上是能够通用的。可是不同的编译器自带的库函数是有区别的。
比方说 GSL 库中代码常常会用到 hypot 函数,这个函数在 MinGW 工具链中属于标准数学库中的函数。能够直接使用。可是 Visual stdio 的数学库中却没有这个函数。因此,直接拿来用 MinGW 的 gsl 是不行的,会在 link 阶段遇到缺少各种函数的定义的问题。
因此,假设想要在 Visual Stdio 环境下使用 GSL 最好的办法是又一次编译 GSL 代码。简单说这又分成了两个不同的道路。
- 使用 Visual Stdio 中的 C 编译器编译 GSL
- 使用 MinGW 中的 C 编译器编译 GSL
当中第一种方法相对来说麻烦些。须要建立个 Visual Stdio project。将相关的代码加入到project中,还须要改动个别几处 gsl 的代码(主要是由于 Visual Stdio 的 C 编译器不支持 C99 标准中的 inline 特性,GSL 代码中有几处不够严谨没有考虑这个问题)。
另外就是 Visual Stdio 的编译器生成 DLL 须要通过 def 文件来指定导出函数,这个 def 文件须要我们自己来生成。gsl 的源码中没有给出。由于这样的方法比較繁琐,对新手来说失败的可能性非常大,因此我今天主要介绍还有一种方法。
使用 MinGW 中的 C 编译器编译 GSL
熟悉 Linux 下软件编译的同学肯定都知道这样的源码编译安装都是标准的三个步骤:
- ./configure
- make
- make install
我们编译 GSL 自然也摆脱不了这个工序。
当然在这之前我们须要有 MinGW 工具链和 MSYS 环境。
在这里我建议大家使用 MSYS2,关于 MSYS2 的安装。能够參考我写的一篇短文:
http://blog.csdn.net/liyuanbhu/article/details/39397931
在工具链准备齐全之后。将 gsl-1.16.tar.gz 解压缩到 MSYS2 的home 文件夹中。之后就是 ./configure 。
这个过程会比較长。可能要用 3、5分钟。
之后在 gsl-1.16 的根文件夹下就会有 config.h 文件了。
对这个文件我们还须要改造一番。之所以要改造是由于这个文件是为 MinGW gcc 工具链准备的,对 Visual Stdio 开发工具来说不是那么适合。
以下就具体的说说这个 config.h 文件的内容。
config.h 的开头 25 行是这样的。
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* Disable deprecated functions and enums while building */
#define GSL_DISABLE_DEPRECATED 1
/* Define if you have inline with C99 behavior */
#define HAVE_C99_INLINE 1
/* Define to 1 if you have the declaration of `acosh', and to 0 if you don't.
*/
#define HAVE_DECL_ACOSH 1
/* Define to 1 if you have the declaration of `asinh', and to 0 if you don't.
*/
#define HAVE_DECL_ASINH 1
/* Define to 1 if you have the declaration of `atanh', and to 0 if you don't.
*/
#define HAVE_DECL_ATANH 1
/* Define to 1 if you have the declaration of `expm1', and to 0 if you don't.
*/
#define HAVE_DECL_EXPM1 1
#define HAVE_C99_INLINE 1 表明编译器支持 C99。尽管 VS 编译器不支持 C99 ,可是这句我们仍然能够保留。 由于 GSL 实际上仅仅是使用到了 C99 的 inline ,其它的特性都没用到。 而 VS 编译器实际上也是支持 inline 的。关于这个 inline 的支持,我们后面还会具体的说。
acosh、asinh、atanh、expm1 这四个函数 VS 的标准库中都是不支持的。
所以要将这四个 1 都改动成 0。
喜欢刨根问底的同学可能会问。VS 中缺少这些函数。会不会影响到 gsl 的功能呢。答案当然是否定的。这些不属于 C 语言标准的函数 GSL 都自己实现了一遍。有些放到了 sys 子文件夹中,有些放到了 utils 文件夹中,还有些分散到其它文件夹。
可是 GSL 实现都在函数名前加了 gsl 前缀。比方 expm1 函数,在 gsl 中实现为:
double gsl_expm1 (const double x)
假设 config.h 中配置为:
#define HAVE_DECL_EXPM1 0
则在 gsl 中全部用的 expm1 函数的地方都会调用 gsl_expm1 函数。
其它须要更改的地方还包含:
#define HAVE_DECL_FINITE 0
#define HAVE_DECL_FREXP 0
#define HAVE_DECL_HYPOT 0
#define HAVE_DECL_ISFINITE 0
#define HAVE_DECL_ISINF 0
#define HAVE_DECL_ISNAN 0
#define HAVE_DECL_LDEXP 0
#define HAVE_DECL_LOG1P 0
#define HAVE_IEEEFP_H 0
#define HAVE_IEEE_DENORMALS 0
#define HAVE_STRDUP 0
#define HAVE_STRINGS_H 0
#define HAVE_UNISTD_H 0
当中 hypot 函数实际上是有的。可是依照 微软的说法:
This POSIX function is deprecated beginning in Visual C++ 2005.
所以我们还是应该不用它。
有了 config.h 文件后我们就能够进行下一步了。
- make
之后是漫长的等待。编译完毕之后我们须要的文件就都有了。
gsl-1.16\cblas.libs 文件夹中能够找到例如以下文件:
- libgslcblas.a
- libgslcblas.dll.a
- libgslcblas-0.dll
gsl-1.16.libs 文件夹中能够找到例如以下文件:
- libgsl.a
- libgsl.dll.a
- libgsl-0.dll
将这些文件都拷贝出来。 .a 文件改名为 .lib。
头文件在 gsl-1.16\gsl 文件夹中,能够将这个文件夹完整拷贝出来。
我们能够建立一个文件夹,名为 gsl-1.16-vs。
然后建立三个子文件夹,各自是 :
- include
- lib
- bin
将头文件所在的 gsl 文件夹拷贝的 include 文件夹中, .lib 文件复制到 lib 文件夹中,.dll 文件复制到 bin 文件夹中。
至此,我们的工作就完毕了大半了。
大家能够试试我们生成的 lib 文件和 dll 文件。具体怎样在 VS 中使用第三方的库。网上介绍文章有非常多,我就不多说了。
gsl 帮助文件里的第一个样例:
#include <stdio.h>
#include <gsl/gsl_sf_bessel.h>
int main(int argc, char *argv[])
{
double x = 5.0;
double y = gsl_sf_bessel_J0 (x);
printf ("J0(%g) = %.18e\n", x, y);
return 0;
}
这个样例如今能够编译通过并顺利的运行了,得到的结果也是正确的。
可是这时我们还不能高兴太早。 Visual Stdio 还给我们预备了一个大坑等着我们往里跳呢。这个大坑就是 VS 编译器不支持 C99。当然也包含不支持 C99 的 inline。
所以在编译以下的代码时就会报很多错误。
#define HAVE_INLINE 1
#include "stdafx.h"
#include <stdio.h>
#include <gsl/gsl_vector.h>
int _tmain(int argc, _TCHAR* argv[])
{
int i;
gsl_vector * v = gsl_vector_alloc (10);
for (i = 0; i < 10; i++)
{
gsl_vector_set (v, i, 1.23 + i);
}
for (i = 0; i < 10; i++)
{
printf ("v_%d = %g\n", i, gsl_vector_get (v, i));
}
gsl_vector_free (v);
getchar();
return 0;
}
报的错误主要是:
error C2054: 在“inline”之后应输入“(”
解决问题就要改动 gsl_inline.h 这个文件。这个文件有例如以下几行。
#ifdef HAVE_INLINE
# if defined(__GNUC_STDC_INLINE__) || defined(GSL_C99_INLINE) || defined(HAVE_C99_INLINE)
# define INLINE_DECL inline /* use C99 inline */
# define INLINE_FUN inline
# else
# define INLINE_DECL /* use GNU extern inline */
# define INLINE_FUN extern inline
# endif
#else
# define INLINE_DECL /* */
#endif
将其改动为 :
#ifdef HAVE_INLINE
# if defined(__GNUC_STDC_INLINE__) || defined(GSL_C99_INLINE) || defined(HAVE_C99_INLINE)
# define INLINE_DECL inline /* use C99 inline */
# define INLINE_FUN inline
# else
# define INLINE_DECL /* use GNU extern inline */
# define INLINE_FUN __inline
# endif
#else
# define INLINE_DECL /* */
#endif
这样再编译就没有错误了。
我将编译好gsl放到了以下的链接里,有须要能够直接下载。
http://download.csdn.net/detail/liyuanbhu/9009755