Conan: C++包管理工具

包管理一直是c/c++项目开发过程中无法触及的痛苦之处。由于出现历史太早,c/c++发展初期并没有现代的包管方案的概念,这部分一直需求一直被忽略。

等到开发者想起要解决这个问题时,发现c/c++与硬件结合太过于紧密,支持太多的平台,在平台适配上有大量的工作需要处理,对于一个编译型语言,需要包管理需要能够正确的处理不同的平台上的二进制兼容问题或者源码编译问题,这导致想基于现有的代码来实现一个现代化的c\c++的包管理系统基本上是一个不可能完成的任务。于是开发者很明智的选择了rust。

尽管如此,人们还是给出了一些折衷的结局方案,包括linx上的apt,mac上的brew,windows上的nutget等方案。其中apt的方案主要解决二进制分发问题,对版本的控制较弱。其中brew/nutget等已经初具一个现代化包管理系统的雏形了。

在这包管理的基础上,出现像conan/vcpkg/buckaroo这样比较优秀的现代化的包管理系统。本文将着重介绍conan的基本概念和主要使用方法。

Conan核心概念

编译配置

在给定版本的源码情况下,的如何来识别构建的二进制是否兼容,对于c\c++程序来说,这是一个不能完成的任务,因为同一份源码,可以获得完全不同的结果,这回设计到编译器/目标平台等各种因素。甚至一个简单的宏开关,都会导致编译的二进制结果时完全不同的。

Conan通过settings/options来识别一个源码编译出来的结果是否为同一份二进制。一个常见的conan配置如下:

> conan profile show default
Configuration for profile default:

[settings]
os=Windows
os_build=Windows
arch=x86_64
arch_build=x86_64
compiler=Visual Studio
compiler.version=15
build_type=Release
[options]
[build_requires]
[env]

即在给定的profile下,对于同一份源码,应该要生成同样的二进制文件。

引用

Conan的另外一个概念叫做reference,reference的组成如下

Pkg/version@user/channel

例如

qt/5.6.3@iceyer/stable
qt/5.6.3@bincrafters/stable
dtkcore/2.0.9@iceyer/stable
dtkcore/2.0.9@iceyer/testing
dtkwidget/2.0.9@iceyer/stable

Pkg和version比较容易理解,代表包名和版本,user代表创建包的用户,stable表示这个包的发布通道。
不同用户创建的包其实没啥关系的,总之一个完整的reference才能代表一份源码。

有了源码和编译配置,我们很自然的想到了,可以用来构建我们需要的二进制了。Conan中的package是指的根据profile构建出来的二进制文件的集合,这个一定要搞清楚。在一个reference下,我们可以根据不同的参数来构建不同的package,如构建不同os的版本,构建包含不同特性的版本等。用一张官方的图来解释下:

Alt text

通过上面的图,聪明的同学一定发现槽点了:conan是基于python的,并且是和cmake强绑定的… 特别是cmake这种面向字符串编程的工具,似乎有着成为c++构建工具的事实标准的趋势发展。

实战Windows静态程序构建

目前Windows下的包管理一直是粗犷的拷贝式管理,这导致了很多工程实践上的不便。通过Conan,我们可以很好来解决这些问题。以dtkcore为例,我们将描述如果构建一个静态的dtkcore。

首先我们需要一个静态构建的qt,这部分比较复杂,这里暂时不展开了,可以通过添加一个remote仓库来使用已经构建好的:

conan remote add iceyer https://api.bintray.com/conan/iceyer/lib

创建包

通过conan new可以创建一个包描述文件,我们在dtkcore源码目录执行如下命令创建一个conan模板文件conanfile.py:

conan new dtkcore/2.0.9@iceyer/stable

通过修改conanfile.py,可以让dtkcore构建可用的二级制包,最终的conanfile.py如下:

from conans import ConanFile, tools


class DtkcoreConan(ConanFile):
    name = 'dtkcore'
    version = '2.0.9'
    license = 'GPL'
    author = 'Iceyer me@iceyer.net'
    url = 'https://github.com/linuxdeepin/dtkcore'
    description = 'cross platform ui library'
    topics = ('qt', 'dtk')
    settings = 'os', 'compiler', 'build_type', 'arch'
    options = {'shared': [True, False]}
    default_options = 'shared=False'
    generators = 'qmake'
    exports_sources = '*'
    requires = 'jom_installer/1.1.2@bincrafters/stable', 'qt/5.6.3@iceyer/stable'

    def extend_include_path(self):
        return '%s/include/libdtk-%s/DCore' % (self.package_folder, self.version)

    # def source(self):
    #     self.run('git clone https://github.com/linuxdeepin/dtkcore.git source')
    #     self.run('cd source && git checkout 2.0.9.9')

    def build(self):
        outdir = self.build_folder
        # includedir = outdir + '/include'
        mkspecsdir = outdir + '/mkspecs'
        # libdir = outdir + '/lib'

        env_vars = tools.vcvars_dict(self.settings)
        env_vars['_CL_'] = '/utf-8'
        with tools.environment_append(env_vars):
            command = 'qmake -r'
            command += ' VERSION=%s' % self.version
            # command += ' CONFIG-=debug_and_release'
            # command += ' CONFIG-=debug_and_release_target'
            command += ' CONFIG+=release'
            command += ' PREFIX=%s' % outdir
            command += ' MKSPECS_INSTALL_DIR=%s' % mkspecsdir
            if self.options.shared == False:
                command += ' DTK_STATIC_LIB=YES'
            command += ' DTK_STATIC_TRANSLATION=YES'
            command += ' DTK_NO_MULTIMEDIA=YES'
            command += ' %s' % self.source_folder
            self.run(command)
            self.run('jom clean')
            self.run('jom')
            self.run('jom install')

    def package(self):
        self.update_path(self.build_folder)

        outdir = self.build_folder
        self.copy('*', dst='include', src=outdir+'/include')
        self.copy('*.lib', dst='lib', src=outdir+'/lib')
        self.copy('*.dll', dst='lib', src=outdir+'/lib')
        self.copy('*', dst='mkspecs', src=outdir+'/mkspecs')

    def package_info(self):
        self.cpp_info.libs = ['dtkcore']
        self.cpp_info.includedirs.append(self.extend_include_path())
        self.env_info.QMAKEPATH = self.cpp_info.rootpath
        self.env_info.QMAKEFEATURES = self.cpp_info.rootpath + '/mkspecs/features'

    def deploy(self):
        self.update_path(self.package_folder)

    def update_path(self, source):
        try:
            content = []
            module_pri = source + '/mkspecs/modules/qt_lib_dtkcore.pri'
            s = open(module_pri)
            for line in s.readlines():
                if line.startswith('QT.dtkcore.tools'):
                    line = 'QT.dtkcore.tools = %s\n' % (
                        self.package_folder + '/bin')
                elif line.startswith('QT.dtkcore.libs'):
                    line = 'QT.dtkcore.libs = %s\n' % (
                        self.package_folder + '/lib')
                elif line.startswith('QT.dtkcore.includes'):
                    line = 'QT.dtkcore.includes = %s\n' % (
                        self.extend_include_path())
                content.append(line)
            s.close()

            # print('create module file', content)
            s = open(module_pri, 'w')
            s.writelines(content)
        except FileNotFoundError:
            print('skip update qt module file')

Conan配置

其中DtkcoreConan属性部分大部分是关于包的一些描述信息,可以不用关心,其中需要注意的几个属性和方法是:

requires

    requires = 'jom_installer/1.1.2@bincrafters/stable', 'qt/5.6.3@iceyer/stable'

这个描述了dtkcore的依赖信息,对与windows来说,有qt就足够了,更复杂的情况,可以通过requires方法来返回不同平台对应的依赖。

build(self)

在命令行执行conan build时,会调用这个方法,需要在这里完成源码的构建。注意这里通过tools.vcvars_dict来导入的windows平台的Visual Studio的构建环境,这个在不同平台需要做相应的处理。当然,对于cmake程序可以使用Conan官方提供的工具,由于dtkcore时通过qmake构建的,这里需要实现构建过程。

Dtkcore在这里主要完成了windows平台编译环境导入和调用qmake/jom进行构建等功能。

package(self)

在命令行执行conan package时,会调用这个目录,将编译结果拷贝到self.package_folder目录。由于dtkcore使用了qt的module功能,这个东西需要绝对路径,所以这里会实现一个update_path将qt_lib_dtkcore.pri里面的路径修改为真实的安装路径。

package_info(self)

在其他程序使用这个库时,会调用这个方法获取库信息,主要可以配置的东西有环境变量已经cpp的构建信息,参考如下:https://docs.conan.io/en/latest/reference/conanfile/attributes.html#cpp-info

Dtk由于要支持qt风格的引入方式,会添加一个额外的includepath,也是通过这里来处理的。

deploy(self)

这里时将包给其他人使用时,使用者将包安装到自己系统路径时调用的方法,注意这里如果需要修改一些路径问题,也会在这里处理。

Conan使用

在构建好dtkcore/dtkwidget后,就可以在其他地方使用了,以deepin-boot-maker为例,在deepin-boot-maker源码中添加一个构建描述文件:

from conans import ConanFile, CMake, tools


class DeepinbootmakerConan(ConanFle):
    name = "deepin-boot-maker"
    version = "2.0.4.8"
    license = ""
    author = " "
    url = ""
    description = ""
    topics = ("", "", "")
    settings = "os", "compiler", "build_type", "arch"
    options = {"shared": [True, False]}
    default_options = "shared=False"
    generators = "qmake"
    requires = 'dtkcore/2.0.9@iceyer/stable', 'dtkwidget/2.0.9@iceyer/stable', 'OpenSSL/1.0.2n@conan/stable', 'jom_installer/1.1.2@bincrafters/stable'

    def build(self):
        outdir = self.build_folder

        env_vars = tools.vcvars_dict(self.settings)
        env_vars['_CL_'] = '/utf-8'
        with tools.environment_append(env_vars):
            command = 'qmake -r'
            command += ' VERSION=%s' % self.version
            # command += ' CONFIG-=debug_and_release'
            # command += ' CONFIG-=debug_and_release_target'
            command += ' CONFIG+=release'
            command += ' PREFIX=%s' % outdir
            command += ' DEFINES+=DTK_STATIC_LIB'
            command += ' DTK_STATIC_TRANSLATION=YES'
            command += ' DTK_NO_MULTIMEDIA=YES'
            command += ' %s' % self.source_folder
            self.run(command)
            # self.run('jom clean')
            self.run('jom')
            self.run('jom install')

然后使用如下命令构建即可:

mkdir build 
cd build
conan install ..  -s compiler.runtime=MT -s arch=x86 -o qt:qtsvg=True -o qt:qttools=True
conan build ..

conan 会自动从远处下载好依赖,并使用依赖进行构建。

总结

Conan很好的解决了c\c++项目中的源码管理和编译配置管理的问题,并提供了强大的中心化二进制管理和分发功能,基于python的conanfile.py的配置文件有及其强大的扩展性,可以很好解决c\c++项目中的二进制管理问题。

参考

dtk项目实现windows下面的conan支持可以参考如下CL:

https://cr.deepin.io/c/dtkcore/+/40789
https://cr.deepin.io/c/dtkwidget/+/40790
https://cr.deepin.io/c/deepin-boot-maker/+/40791

conan官方文档:

https://docs.conan.io/

Dockerfile代理设置

环境准备

本机环境

  1. shadowsocks
  2. privoxy
  3. docker

配置

连接shadowsocks

sslocal -s {proxy_server} -p {proxy_server_port} -l 7070 -k {key} -m aes-256-cfb -v

7070为本地端口号,在配置privoxy中需要用到

配置privoxy

  1. 修改监听地址

找到/etc/privoxy/config (不同发行版本可能不同), 修改listen-address为:

listen-address  0.0.0.0:8118
  1. 修改转发地址

找到forward-socks5t,修改为

forward-socks5t   /               127.0.0.1:7070 .

7070为shadowsocks代理地址

配置Dockerfile

在需要代理的地方添加

ENV http_proxy http://{host_ip}:8118
ENV https_proxy http://{host_ip}:8118

在代理使用结束后一定要取消代理

ENV http_proxy ""
ENV https_proxy ""

静态编译Qt SDK使用时的一点小坑:无法使用动态库

最近在移植Dtk的过程中移植遇到一个比较困扰的问题, 就是编译的动态库链接失败,错误如下:

main.obj : error LNK2019: 无法解析的外部符号 “__declspec(dllimport) public: __thiscall DtkSharedLibary::DtkSharedLibary(void)” (__imp_??0DtkSharedLibary@@QAE@XZ),该符号在函数 _main 中被引用


#if defined(DTKSHAREDLIBARY_LIBRARY)
#  define DTKSHAREDLIBARYSHARED_EXPORT Q_DECL_EXPORT
#else
#  define DTKSHAREDLIBARYSHARED_EXPORT Q_DECL_IMPORT
#endif

class DTKSHAREDLIBARYSHARED_EXPORT DtkSharedLibary
{

public:
    DtkSharedLibary();
};

想了好久也没有得出结论,于是自己写了个小的测试项目,发现是因为自己的Qt SDK是为了Deepin Boot Maker做的静态编译版本,导致Q_DECL_EXPORT失效,解决办法如下:


# 在引用库的pro文件添加如下定义

DEFINES += STATIC_LIB


// 在头文件中将导出宏定义为空
#if defined(STATIC_LIB)
#  define DTKSHAREDLIBARYSHARED_EXPORT
#else
#if defined(DTKSHAREDLIBARY_LIBRARY)
#  define DTKSHAREDLIBARYSHARED_EXPORT Q_DECL_EXPORT
#else
#  define DTKSHAREDLIBARYSHARED_EXPORT Q_DECL_IMPORT
#endif
#endif
class DTKSHAREDLIBARYSHARED_EXPORT DtkSharedLibary
{

public:
    DtkSharedLibary();
};

另外,这个问题在非window平台不会出现。

附件中为测试代码:

StaticQtWithSharedLibrary

Refs:

How to build Qt 5.5.0 static with angel

Build Tools:

  • Windows 7
  • Visual Studio 2010 SP1
  • DirectX SDK
  • Python 3.5
  • Perl 5.2

1 Edit qtbase config

Open qtbase/mkspecs/common/msvc-desktop.conf

Change MD too MT

2 Set DirectX env

Open CMD, and run:

%comspec% /k ""D:\Dev\Microsoft DirectX SDK (June 2010)\Utilities\Bin\dx_setenv.cmd"" x86

3 Set VS2010 env

Run:

%comspec% /k ""C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"" x86

4 Build Qt 5.5.0

Change directory to qt source root, and run:

configure -prefix "D:\Dev\QtStatic\5.5.0\vs2010" -debug-and-release -platform win32-msvc2010 -no-qml-debug -confirm-license -opensource -static -qt-pcre -no-icu -no-sql-sqlite -no-nis -no-cups -no-iconv -no-dbus -nomake examples -no-sql-odbc -no-compile-examples -skip qtwebkit -skip qtwebkit-examples -skip qtactiveqt -no-openssl -qt-zlib -qt-libpng -qt-freetype -qt-libjpeg -opengl es2 -angle

Update:

For 5.6.3

configure -prefix "D:\Develop\Qt\Static\5.6.3\vs2017" -static-runtime -release -platform win32-msvc2017 -target xp -confirm-license -opensource -static -no-wmf-backend -nomake examples -nomake tests -skip qtwebengine -angle -opengl es2

整型序列排序到底有多快?

稍微对算法研究的人应该知道,在通用算法中,随机快速排序有着最好的时间效率,为O(nlgn)。但是如果对问题做一个限制,假设输入的序列长值为32位的无符号型整数,那么对这样的序列进行排序,时间复杂度是多少了?

为了处理这个问题,我们首先要介绍两种不是太常用的排序:计数排序以及稳定排序。

一、计数排序

计数排序是一种典型的空间换时间的排序方案。假设排序序列为:

A {x1, x2, x3, …, xn}

并且对A中任意元素xi;满足xi<k;

解决方案:


count = int[k]
for v < --n
  count[v] ++
  icount = k;

for i in [n, ..., 1]
  while(k)
    if count[k] > 0
      B[i] = k
      count[k]--
      break
else
  k--

计数排序的时间复杂度为O(n)实际上需要考虑xi<k这一个强限制条件,其时间效率为T(n)=k+n

对于32位整型,k达到4亿大小,导致这个排序在时间以及空间复杂度上是不可行的。

但是,如果是8位整型,k=256,那么这个算法将有着极高的效率。

那么我们可以将一个32整型分解为4个8位整型,如果有一种算法能够对4个一组的数据进行快速排序,那么这个问题就迎刃而解了。

幸运的是,我们能够找到这样的一个算法,这要感谢IBM的创始人。

稳定排序

稳定排序就是这样一种算法。稳定排序又叫尾排序,即排序时先排列最低位。

对于一个整型,需要使用4次稳定排序才能将数据进行重排。

综合两种算法,对整型的最快排序为时间为:

1024+4n

这是一个T(n)=O(n)的算法。

在Windows7 X64 上安装Tiny Issue

安装准备:
Windows 7 X64
PHP Version 5.5.6 (Win64)
http://windows.php.net/download/#php-5.5-ts-VC11-x64
VC11 x64 Thread Safe
Apache/2.4.7 (Win64)
http://www.apachelounge.com/download/
httpd-2.4.7-win64-VC11.zip
MySQL
tinyissue

一、Apache安装配置

1.解压httpd-2.4.7-win64-VC11.zip 到 D:DevApache24并创建 D:www 目录

2.修改配置文件httpd.conf
(1)ServerRoot “c:/Apache24” 改为 ServerRoot “D:/Apache24”; //Apache程序的位置。
(2)ServerName 前面的“#”号去掉;
(3)DocumentRoot “c:/Apache24/htdocs” 改为DocumentRoot “D:/www”; //网站的根目录
(4)<Directory “c:/Apache24/htdocs”> 改为<Directory ” D:/www “>;
(5)DirectoryIndex index.html 改为DirectoryIndex index.html index.php index.htm //支持更多的默认页
(6)ScriptAlias /cgi-bin/ “c:/Apache24/cgi-bin/” 改为ScriptAlias /cgi-bin/ “d:/dev/Apache24/cgi-bin/”
(7)<Directory “c:/Apache24/cgi-bin”> 改为<Directory “D:/Apache24/cgi-bin”>

3.安装服务

httpd.exe -k install -n "Apache Server 2.4"

在services.msc中找到服务并开启服务。

4.测试Apache服务器
访问 http://localhost/
出现It works!表明安装基本正常

二、PHP安装设置

1.解压php-5.5.6-Win32-VC11-x64.zip到D:DevPHP

2.复制份php.ini-development,并改名为PHP.ini。

3.打开Apache24下httpd.conf,在最后加上

# php5 support
LoadModule php5_module "D:/Dev/PHP/php5apache2_4.dll"
AddHandler application/x-httpd-php .php
# configure the path to php.ini
PHPIniDir "D:/Dev/PHP/"

php5apache2_4.dll使用php-5.5.6-Win32-VC11-x64.zip中自带的即可。

4. 重启 Apache 服务器。

5.在www目录下新建一个index.php,内容为<?php phpinfo(); ?>
访问http://localhost/index.php
出现php的信息就说明php已经成功安装。

三、MYSQL安装

1.从http://dev.mysql.com/downloads/mysql/
下载64位社区版本的MYSQL。

2.打开MYSQL Workbench,添加新用户tinyissue,和新数据库tinyissue-db

3.配置PHP.ini识别MYSQL
(1)extension_dir = “ext”,去掉前面的“;”,并改为
extension_dir =”D:DevPHPext”
(2)添加如下两行
extension=php_mysql.dll
extension=php_mysqli.dll
重启Apache服务(通过services.msc)

四、安装tinyissue

1. 下载tinyissue,解压到D:wwwtinyissue

2. 修复与PHP5.5.6兼容问题
yield在高级版本中变成了关键字,不能将自定义函数命名为yield。
(1)找到Section.php,修改

public static function yield()
{
return static::yield(static::stop());
}

public static function yield_section()
{
return static::sections_yield(static::stop());
}

(2)找到helper.php,修改

function yield($section)
{
    return LaravelSection::yield($section);
}

function helper_yield($section)
{
    return LaravelSection::sections_yield($section);
}

3.配置Apache,修改httpd.conf,增加

<Directory "E:/www/tinyissue">
    AllowOverride None
    Options None
    Order allow,deny
    Allow from all
    Options Indexes MultiViews ExecCGI FollowSymLinks
    DirectoryIndex index.php
</Directory>

4.访问http://localhost/tinyissue/install/进行安装

5.OK~

使用单件构造器(SingletonConstructor)顺序初始化单件

使用单件构造器(SingletonConstructor)保证单件顺序初始化

对于单件模式时,目前主要有两种方法,即静态初始化以及动态初始化
对于动态初始化,由于锁(Double Check Lock)的加入,必然无法应用于高性能要求的场合。
对于静态初始化,单件的初始化顺序是不能保证的,如果单件直接存在依赖关系,这将导致初始化失败。
为了解决这个问题,引入一个单件构造器,由单件构造器来负责对各个单件进行显式的初始化

单件构造器(SingletonConstructor)定义如下:
1.单件构造器是所有其他单件的友元。
2.单件构造器是所影响域存在的唯一的静态初始化单件。

//Head file
class SingletonConstructor{
private:
    SingletonConstructor();
    ~SingletonConstructor();
    SingletonConstructor(SingletonConstructor const&);
    SingletonConstructor& operator=(SingletonConstructor const&);

    static SingletonConstructor s_SingletonConstructor_;
};

calss SingletonA{
public:
    friend class SingletonConstructor;
    SingletonA& Instance()
    {
        return *s_SingletonA_;
    }
private:
    SingletonA()
    {
        if (!s_SingletonA_)
            s_SingletonA_ = this;
    }

    ~SingletonA();
    SingletonA(SingletonA const&);
    SingletonA& operator=(SingletonA const&);

    static SingletonA *s_SingletonA_;
}

calss SingletonB{
public:
    friend class SingletonConstructor;
    SingletonB& Instance()
    {
        return *s_SingletonC_;
    }
private:
    SingletonB()
    {
        if (!s_SingletonB_)
        s_SingletonB_ = this;
    }
    ~SingletonB();
    SingletonB(SingletonB const&);
    SingletonB& operator=(SingletonB const&);
    
    static SingletonB *s_SingletonB_;
}

calss SingletonC{
public:
    friend class SingletonConstructor;
    SingletonC& Instance()
    {
    return *s_SingletonC_;
    }
private:
    SingletonC()
    {
        if (!s_SingletonB_)
        s_SingletonB_ = this;
    }
    ~SingletonC();
    SingletonC(SingletonC const&);
    SingletonC& operator=(SingletonC const&);
    static SingletonC *s_SingletonC_;
}

//Cpp file
SingletonConstructor SingletonConstructor::s_SingletonConstructor_;

SingletonConstructor::SingletonConstructor()
{
    m_SingleA = new SingletonA();
    m_SingleB = new SingletonB();
    m_SingleC = new SingletonC();
    //Or you can use std::vector<SingletonBase *> instead of m_Single*
}

SingletonConstructor::~SingletonConstructor()
{
    delete m_SingleC;
    delete m_SingleB;
    delete m_SingleA;
    //Or you can use std::vector<SingletonBase *> instead of m_Single*
}

单件构造器的思想在于显式初始化各个单件。

freeglut 简明安装指南 for windows

glut(GL Unity Tookit)是OpenGL的一套辅助开发函数库,但是由于缺乏更新以及非开源等原因,目前已经有12年没有维护,所以强烈建议更换到freeglut这个完全兼容的开源代替版本。

一、下载freeglut

freeglut的最新版本是2.8.1(Released: 5 April 2013)
可以从http://freeglut.sourceforge.net/获得其最新版本。

二、编译freeglut

freeglut提供了对windows平台良好的编译支持,在freeglut-2.8.1VisualStudio目录下可以找到2008~2012版本的VS工程文件(经测试,VS2013也完全可以使用VS2012的工程文件)。
2.1. 打开对于版本的工程文件,选择对应的配置版本,建议Realese版本。
2.2. 生成–>生成解决方案
2.3. 生成文件在freeglut-2.8.1libx86目录,有freeglut.lib, freeglut.dll.

三、安装freeglut

3.1. Header文件安装:
将freeglut-2.8.1includeGL 目录复制到 Microsoft Visual Studio 12.0VCinclude目录下。
3.2. 库文件安装
将freeglut.lib文件复制到Microsoft Visual Studio 12.0VClib目录下。
将freeglut.dll文件复制到C:WindowsSysWOW64 【32位系统为 “C:WindowsSystem32”】目录下。

四、使用freeglut

直接包含<gl/freeglut.h>文件即可。

Qt 5.1 编译中处理《error C2059: 语法错误:“常量”》错误

新装了VS2013的环境,由于是64位系统并且Qt对VS2013支持不好,需要自己编译一下,但是编译过程中出现了些坑爹的问题:

error C2059: 语法错误:“常量”

报错的文件是win-math.h,代码如下:

enum {
  FP_NAN,
  FP_INFINITE,
  FP_ZERO,
  FP_SUBNORMAL,
  FP_NORMAL,
};

这段代码毫无疑问没有任何问题,但是FP_NAN这个宏可就有问题了,
FP_NAN在VS2013的标准头文件math.h被定义成了
#define _NANCODE 2
#define FP_NAN _NANCODE

结果这段代码就变成了:

enum{
  2,
  FP_INFINITE,
  FP_ZERO,
  FP_SUBNORMAL,
  FP_NORMAL,
};

其他几个枚举值也有同样的问题,解决方法有两个:
一是删除这个枚举,毕竟在math.h中已有定义。
二是使用#undef:

#undef FP_NAN
#undef FP_INFINITE
#undef FP_ZERO
#undef FP_SUBNORMAL
#undef FP_NORMAL
enum FP_MATH_{
	FP_NAN,
	FP_INFINITE,
	FP_ZERO,
	FP_SUBNORMAL,
	FP_NORMAL,
};