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

    xiaoxiao2021-03-25  113

    前言Qt 的安装 选择对应版本更改文件的属性使得其具有可执行性执行程序检查 Qt Creator 是否勾选进入 Qt Creator Qt 环境的配置 安装 gcc 或 g 编译器编译器错误 ICE in gen_type_die_with_usage更新 g C 在 MSVC 与 GNU G上的不同之处 enum 的前置声明size_t 与 stdsize_tauto 推断函数返回值函数返回数组问题 其他问题 初始化顺序 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 是否勾选。

    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 一栏。

    2. 编译器错误 ICE: in gen_type_die_with_usage

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

    3. 更新 g++

    根据这里的帖子,需要输入以下的命令来获取最新的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

    还没完,还需要将新的 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查看版本判断是否成功

    再次编译,结果遗憾的是,编译器又再次返回了 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++ 在 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<Machine *> &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 <cstdlib> /* ... */ 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 中可以看到。欢迎讨论。 如果本文有错误的地方,欢迎大家指正。谢谢大家。


    转载请注明原文地址: https://ju.6miu.com/read-14595.html

    最新回复(0)