将 C++ Qt 程序移植至 Linux 系统

  • 前言
  • Qt 的安装
    • 选择对应版本
    • 更改文件的属性使得其具有可执行性
    • 执行程序
    • 检查 Qt Creator 是否勾选
    • 进入 Qt Creator
  • Qt 环境的配置
    • 安装 gcc 或 g 编译器
    • 编译器错误 ICE in gen_type_die_with_usage
    • 更新 g
  • C 在 MSVC 与 GNU G上的不同之处
    • enum 的前置声明
    • size_t 与 stdsize_t
    • auto 推断函数返回值
    • 函数返回数组问题
  • 其他问题
    • 初始化顺序 warning will be initialized after -Wreorder
    • 替换 INT_MAX 和 INT_MIN
  • 感想

前言

前段时间,我一直在使用 Visual Studio 编写一个 C++ Qt 游戏小程序:https://github.com/lgasyou/SimpleSimulator。突然有一天,我想看看它的可移植性到底如何,于是我把它转化成 .pro 并移至了 Ubuntu 14.04 系统中,之后就开始了漫长的配置之旅。

Qt 的安装

1. 选择对应版本

可以前往 Qt 的官方网站根据CPU架构类型和版本号的不同选择 qt-opensource-linux-CPU架构类型-任意版本号.run 下载,例如需在 32 位系统下下载版本号为 5.8.0 时的 Qt 则选择 qt-opensource-linux-x86-5.8.0.run。

2. 更改文件的属性使得其具有可执行性:

刚刚下载的程序并不具有可执行性,还需要改变它的属性。
在终端中切换目录至文件路径,之后执行 chmod +x qt-opensource-linux-CPU架构类型-任意版本号.run

3. 执行程序

在终端中输入./qt-opensource-linux-CPU架构类型-任意版本号.run,例如qt-opensource-linux-x86-5.8.0.run 或是直接双击执行程序。

4. 检查 Qt Creator 是否勾选。

将 C++ Qt 程序移植至 Linux 系统_第1张图片

5. 进入 Qt Creator

安装完成,可以使用菜单栏的搜索功能搜索并进入 Qt Creator(Community)。如果需要可以选择将其固定至菜单栏之上。之后便进入了下一个环节。

Qt 环境的配置

1. 安装 gcc 或 g++ 编译器

安装完成,我创立了一个 HelloWorld 程序,并开始编译。但是很不幸运,编译器返回了 “error while building deploying project” 的错误。我在网上找了很久,但是解决方法都与我的情况基本无关。直到好几天后,我突然发现我的 Compiler 配置里面竟然没有 C++ 的编译器!于是我又安装了 g++: sudo apt-get install g++。终于,可以开始编译了!但是没有10秒钟,我又遇到了第二个问题。
另附:该页的打开方式为:Qt Creator 的菜单栏 -> Tools -> Options -> Build & Run -> Compilers。安装后会自动识别并显示在 Auto-detected 一栏。
将 C++ Qt 程序移植至 Linux 系统_第2张图片

2. 编译器错误 ICE: in gen_type_die_with_usage

这次,编译器又返回了 ICE: in gen_type_die_with_usage, at dwarf2out.c:19484 的错误。我上网一查,这次竟然是编译器的 bug。是 g++ 版本的问题,需要更新。

3. 更新 g++

  1. 根据这里的帖子,需要输入以下的命令来获取最新的g++:

    sudo add-apt-repository ppa:ubuntu-toolchain-r/test
    sudo apt-get update

    (注:在这里,我执行完这两个语句后依然提醒:
    E: Unable to locate package g++-5
    E: Couldn’t find any package by regex ‘g++-5’,
    E: Unable to locate package gcc-5
    E: Couldn’t find any package by regex ‘gcc-5’,
    但是再次执行就成功了,所以如果一次没成功不妨多试几次,要是还是不行就再查找其他解决方案也不迟)

    sudo apt-get install gcc-5 g++-5

  2. 还没完,还需要将新的 g++-5 链接至 Qt 所调用的 link 文件(这个东西就和快捷方式差不多):

    sudo ln -s /usr/local/bin/gcc /usr/bin/gcc
    sudo ln -s /usr/local/bin/g++ /usr/bin/g++

    可以通过 g++ --version查看版本判断是否成功

  3. 再次编译,结果遗憾的是,编译器又再次返回了 usr/bin/ld: cannot find -lGL 的错误。再次上网查找,最终找到了通用的缺失 -lxxx 的解决方法:http://eminzhang.blog.51cto.com/5292425/1285705。对于我的环境而言而言,这个是系统缺失 libGL.so 的问题,也就是缺失 OpenGL 导致的问题。于是有:

    (注:可能需要安装 apt-file 程序并更新它的数据:)
    sudo apt-get install apt-file
    apt-file update

    apt-cache search libgl-dev (注:寻找哪个包中包含该库)
    sudo apt-get install libgl1-mesa-dev(注:这是我的系统上返回的第一项)

最终配置大致为:
将 C++ Qt 程序移植至 Linux 系统_第3张图片
再次编译,几十秒内没有报错。

C++ 在 MSVC 与 GNU G++上的不同之处

很遗憾,它不肯善罢甘休,很快,我又遇到了多个 Error。

1. enum 的前置声明

之前有:

namespace Gameconstants {
enum BuildingTypes;
}

这种用法标准的 C++ 是不支持的。我猜想是微软扩充的结果。

对此有两种解决方法:

// 直接包含头文件
#include "gameconstants.h"

/* 使用 enum BuildingTypes */

或者,在 C++11 或之后版本:

// C++11 规定,枚举的前置声明需要指定成员类型,例如本例中指定为int。
// 同时,若是 enum class 类型,则可以不指定,使用默认类型 int。
namespace Gameconstants {
enum BuildingTypes : int;
}

2. size_t 与 std::size_t

以往,我都是直接使用 size_t 的:

size_t member;

但是在跨平台时,我又发现实际上 size_t 是定义在 std 命名空间中的。根据查找,size_t 是定义于 vcruntime.h 的。很明显,这也是微软扩充的结果。所以,通用的写法应该是:

std::size_t member;

3. auto 推断函数返回值

我在 baseindustry.h 中有:

const auto &machines() const { return this->machines_; }

但是,根据编译器提示这个是 C++14 之后才支持的,由于不想再次更新 g++,替换为

const std::vector &machines() const { return this->machines_; }

4. 函数返回数组问题

public:
    auto occupiedMap() const { return this->occupiedMap_; }

private:
    bool occupiedMap_[100][100];

在 VS 上编译成功,在 g++ 上失败。我本想使用 typedef 代替:

typedef bool Array[100][100];
Array occupiedMap() const { return this->occupiedMap_; }

但编译仍然失败,最终经过查找,由于数组不支持复制,所以 C++ 不允许返回数组。经过 《C++ Primer》 的启发,我将它改为了尾置返回类型:

// 返回了数组的指针
auto occupiedMap() const -> const bool(*)[100] {
    return occupiedMap_;
}

相比而言,确实清晰明了。

最后的最后,程序终于成功运行。

其他问题

1. 初始化顺序, warning: will be initialized after [-Wreorder]

当时由于某个类成员变动频繁,类的构造函数中的成员顺序与声明时不同,所以出现了这个错误。


// 会返回 warning: memberOne will be initialized after [-Wreorder]
class A {
public:
    A() : memberOne(), memberTwo()
    { }

private:
    Type memberTwo;
    Type memberOne;
}

应改为:

// 没有报警
class B {
public:
    B() : memberOne(), memberTwo()
    { }

private:
    Type memberOne;
    Type memberTwo;
}

2. 替换 INT_MAX 和 INT_MIN

在原版的代码中,我是这样写的

    #include 
    /* ... */
    namespace GameConstants {
    const int integerMaximum = INT_MAX;
    const int integerMinimum = INT_MIN;
    }

但在当时,不知道为何,编译时报错:INT_MAX 与 INT_MIN 未定义。没办法,我将他们替换成了:

const int integerMinimum = (1 << (sizeof(int) * 8 - 1));
const int integerMaximum = (int)((unsigned)integerMinimum - 1);

这是与平台无关的最大最小值。使用了位运算,具体实现请参考计算机组成原理。
另附:刚刚在 Linux 系统的 stdlib.h 中看到了 INT_MAX 与 INT_MIN 这两个宏定义。也就是说它们是 C 标准定义的。

感想

能够成功运行,着实不易。虽然过程很艰难,不过通过这个过程也学到了不少干货,很值。
接下来我的重点仍然在开头提到的游戏:https://github.com/lgasyou/SimpleSimulator。以上提到的改变均能在 Commits 中可以看到。欢迎讨论。
如果本文有错误的地方,欢迎大家指正。谢谢大家。


你可能感兴趣的:(Qt5)