前段时间,我一直在使用 Visual Studio 编写一个 C++ Qt 游戏小程序:https://github.com/lgasyou/SimpleSimulator。突然有一天,我想看看它的可移植性到底如何,于是我把它转化成 .pro 并移至了 Ubuntu 14.04 系统中,之后就开始了漫长的配置之旅。
可以前往 Qt 的官方网站根据CPU架构类型和版本号的不同选择 qt-opensource-linux-CPU架构类型-任意版本号.run 下载,例如需在 32 位系统下下载版本号为 5.8.0 时的 Qt 则选择 qt-opensource-linux-x86-5.8.0.run。
刚刚下载的程序并不具有可执行性,还需要改变它的属性。
在终端中切换目录至文件路径,之后执行 chmod +x qt-opensource-linux-CPU架构类型-任意版本号.run
。
在终端中输入./qt-opensource-linux-CPU架构类型-任意版本号.run
,例如qt-opensource-linux-x86-5.8.0.run
或是直接双击执行程序。
安装完成,可以使用菜单栏的搜索功能搜索并进入 Qt Creator(Community)。如果需要可以选择将其固定至菜单栏之上。之后便进入了下一个环节。
安装完成,我创立了一个 HelloWorld 程序,并开始编译。但是很不幸运,编译器返回了 “error while building deploying project” 的错误。我在网上找了很久,但是解决方法都与我的情况基本无关。直到好几天后,我突然发现我的 Compiler 配置里面竟然没有 C++ 的编译器!于是我又安装了 g++: sudo apt-get install g++
。终于,可以开始编译了!但是没有10秒钟,我又遇到了第二个问题。
另附:该页的打开方式为:Qt Creator 的菜单栏 -> Tools -> Options -> Build & Run -> Compilers。安装后会自动识别并显示在 Auto-detected 一栏。
这次,编译器又返回了 ICE: in gen_type_die_with_usage, at dwarf2out.c:19484 的错误。我上网一查,这次竟然是编译器的 bug。是 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 updateapt-cache search libgl-dev (注:寻找哪个包中包含该库)
sudo apt-get install libgl1-mesa-dev(注:这是我的系统上返回的第一项)
很遗憾,它不肯善罢甘休,很快,我又遇到了多个 Error。
之前有:
namespace Gameconstants {
enum BuildingTypes;
}
这种用法标准的 C++ 是不支持的。我猜想是微软扩充的结果。
对此有两种解决方法:
// 直接包含头文件
#include "gameconstants.h"
/* 使用 enum BuildingTypes */
或者,在 C++11 或之后版本:
// C++11 规定,枚举的前置声明需要指定成员类型,例如本例中指定为int。
// 同时,若是 enum class 类型,则可以不指定,使用默认类型 int。
namespace Gameconstants {
enum BuildingTypes : int;
}
以往,我都是直接使用 size_t 的:
size_t member;
但是在跨平台时,我又发现实际上 size_t 是定义在 std 命名空间中的。根据查找,size_t 是定义于 vcruntime.h 的。很明显,这也是微软扩充的结果。所以,通用的写法应该是:
std::size_t member;
我在 baseindustry.h 中有:
const auto &machines() const { return this->machines_; }
但是,根据编译器提示这个是 C++14 之后才支持的,由于不想再次更新 g++,替换为
const std::vector &machines() const { return this->machines_; }
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_;
}
相比而言,确实清晰明了。
最后的最后,程序终于成功运行。
当时由于某个类成员变动频繁,类的构造函数中的成员顺序与声明时不同,所以出现了这个错误。
// 会返回 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;
}
在原版的代码中,我是这样写的
#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 中可以看到。欢迎讨论。
如果本文有错误的地方,欢迎大家指正。谢谢大家。