【问题标题】:What's your favorite way of dealing with cross-platform development? [closed]您最喜欢处理跨平台开发的方式是什么? [关闭]
【发布时间】:2009-02-25 15:06:23
【问题描述】:

我目前正在开发跨平台应用程序,只是好奇其他人如何解决以下问题:

  • 字节序
  • 浮点支持(某些系统在软件中模拟,非常慢)
  • I/O 系统(即显示、声音、文件访问、网络等)
  • 当然还有大量的编译器差异

显然,这是针对诸如 c/c++ 之类的语言,它们不抽象大部分这些东西(不像 java 或 c#,它们在很多系统上不受支持)。

如果你好奇的话,我正在开发的系统是 Nintendo DS、Wii、PS3、XBox360 和 PC。


编辑
这里有很多非常好的答案,从如何自己处理差异,到图书馆建议(甚至只是放弃和使用葡萄酒的建议)。我实际上并不是在寻找解决方案(已经有一个),但只是好奇其他人如何解决这种情况,因为看到其他人如何思考/编码总是很好的,这样你就可以继续发展和成长。

这是我解决问题的方法(如果您没有从上面的系统列表中猜到,我正在开发控制台/Windows 游戏)。请记住,我所使用的系统通常没有为它们编写的跨平台库(Sony 实际上建议您从头开始编写自己的渲染引擎并只使用他们的 OpenGL 实现,这并不完全遵循无论如何标准,作为参考)。

字节序
我们所有的资产都可以为每个系统定制。我们所有的原始数据(纹理除外)都存储在 XML 中,我们在构建项目时将其转换为系统特定的二进制格式。鉴于我们是如何为游戏机开发的,我们不需要担心数据在不同字节序格式的平台之间传输(只有 PC 允许用户这样做,因此它也与其他系统隔离) .

浮点支持
大多数现代系统都可以很好地处理浮点值,但 Nintendo DS(和 GBA,但如今对我们来说几乎是一个死平台)是个例外。我们通过 2 个不同的类来处理这个问题。第一个是“定点”类(模板化,可以指定要使用的整数类型以及十进制值的位数),它实现所有算术运算符(处理位移)并自动进行类型转换。第二个是“浮点”类,它基本上只是对浮点数的一个包装,唯一的区别是它还实现了移位运算符。通过实现移位运算符,我们可以在 DS 上使用移位进行快速乘法/除法,然后无缝过渡到使用浮点数更好的平台(如 XBox360)。

I/O 系统
这对我们来说可能是最棘手的问题,因为每个系统都有自己的控制器输入、图形方法(XBox360 使用 DirectX9 的变体,PS3 有 OpenGL,或者你可以从头开始编写自己的,DS 和 Wii 有自己的专有系统)、声音和网络(实际上只有 DS 在协议上有很大不同,但它们都有自己的服务器系统,您必须使用)。

我们最终解决这个问题的方法是简单地为每个系统编写相当高级的包装器(例如,用于图形的网格、用于控制器的键映射系统等)并让所有系统使用相同的头文件进行访问.然后只需为每个平台编写特定的 cpp 文件(从而形成“引擎”)。

编译器差异
这是一件不容易解决的事情,当我们遇到编译器问题时,我们通常会在本地 wiki 上记录信息(以便其他人可以看到要注意的内容和解决方法),如果可能,编写一个宏来为我们处理这种情况。虽然它不是最优雅的解决方案,但它可以工作并且看到一些编译器在某些地方是如何简单地损坏的,更优雅的解决方案往往会破坏编译器。 (我只希望所有的编译器都实现了微软的“#pragma once”命令,比把所有东西都包装在#ifdef 里简单多了)

【问题讨论】:

    标签: c++ c cross-platform


    【解决方案1】:

    这种复杂性的很大一部分通常由您使用的第三方库(最有名的库)解决。很少有人从头开始写所有东西...

    【讨论】:

    • 我同意你的观点,使用多平台库是处理这个问题的好方法
    【解决方案2】:

    对于从文件加载的数据中的字节序问题, 在文件头中嵌入一个值,例如 0x12345678。

    加载数据的对象,查看这个值,如果它匹配它的值的内部表示,那么文件包含本机字节序值。从那里开始负载很简单。

    如果值不匹配,那么它是一个外部字节序,所以加载器需要在存储它们之前翻转值。

    【讨论】:

      【解决方案3】:

      我通常将系统特定的调用封装在一个类中。如果您决定将应用程序移植到新平台,您只需移植一个文件...

      【讨论】:

        【解决方案4】:

        我通常使用多平台库,如 boost 或 Qt,它们解决了我处理平台特定代码的 95% 的问题(我承认我处理的唯一平台是 win-xp 和 linux)。对于剩下的 5%,我通常将平台特定代码封装在一个或多个类中,使用工厂模式或泛型编程来减少 #ifdef/#endif 部分

        【讨论】:

          【解决方案5】:

          我认为其他答案已经很好地解决了除了字节序之外的所有问题,所以我将对此添加一些内容......这应该只是你与外界的接口的问题。您所有的内部数据处理都应该以本机字节顺序完成。当通过 TCP/IP(或任何其他套接字协议)进行通信时,您应该始终使用一些函数来将您的值与网络字节顺序进行转换。 IIRC,功能是htons()htonl(),(主机到网络短,主机到网络长)及其相反,我不记得了......也许像ntohl() 等?

          您应该与具有错误字节顺序的数据进行交互的唯一其他地方是从本地硬盘驱动器读取文件,因此让您的文件加载器和写入器使用类似的功能(也许您甚至可以使用网络功能)。

          通过使用这些库提供的函数来始终处理字节序(即使在您从未打算移植的代码中也使用它们,除非您有令人信服的理由不这样做——稍后当您决定移植时,它会变得更容易) ,您可以在任何平台上运行代码,它会“正常工作”,无论原生字节序如何。

          【讨论】:

            【解决方案6】:

            通常,这种可移植性问题留给检测特定系统的构建系统(在我的情况下为 autotools 或 cmake)。最后,我从这个构建系统中获得了一个 config.h,然后我只需要使用在这个头文件中定义的常量(使用 IF DEFINED)。

            例如这里是一个 config.h :

            /* Define to 1 if you have the <math.h> header file. */
            #define HAVE_MATH_H
            
            /* Define to 1 if you have the <sys/time.h> header file. */
            #define HAVE_SYS_TIME_H
            
            /* Define to 1 if you have the <errno.h> header file. */
            #define HAVE_ERRNO_H
            
            /* Define to 1 if you have the <time.h> header file. */
            #define HAVE_TIME_H
            

            然后代码将如下所示(例如 time.h):

            #ifdef (HAVE_TIME_H)
            //you can use some function from time.h
            #else
            //find another solution :)
            #endif
            

            【讨论】:

              【解决方案7】:

              对于数据格式 - 对所有内容都使用纯文本。对于编译器差异,请注意 C++ 标准并使用编译器开关,例如 g++ -pedantic,它会警告您存在可移植性问题。

              【讨论】:

                【解决方案8】:

                这取决于你正在做的事情。几乎总是正确的选择是将基本内容移植到任何目标平台,然后使用通用 API 处理它。

                例如,我做了很多数值计算编码,一些平台有很多损坏/非标准代码:解决它的方法是重新实现这些函数,然后在代码中的任何地方使用这些新函数(对于可以工作的平台,新函数只是调用旧函数)。

                但这仅适用于低级别的东西。对于 GUI、高级 IO,几乎每次都使用已经存在的库绝对是更好的选择。

                【讨论】:

                  【解决方案9】:

                  对于没有原生浮点支持的平台,我们使用了一些自己的定点类型和一些 typedef。像这样:

                  // native floating points
                  typedef float Real;
                  

                  或者对于固定点,例如:

                  typedef FixedPoint_16_16 Real;
                  

                  那么数学函数可能看起来像这样:

                  Real OurMath::ourSin(const Real& value);
                  

                  实际的实现当然可能是:

                  float OurMath::ourSin(const float& value)
                  {
                    return sin(value);
                  }
                  // for fixed points something more or less trickery stuff
                  

                  【讨论】:

                    【解决方案10】:

                    对于像字节顺序这样的事情,使用不同的函数或类有点开销。尝试使用预处理器,例如:

                    #ifdef INTEL_LINUX:
                       code here
                    #endif
                    
                    #ifdef SOLARIS_POWERPC
                       code here
                    #endif
                    

                    【讨论】:

                    • 做很多 Solaris PowerPC 编码? :)
                    • -1:测试功能,而不是特定平台
                    • @steveth45:不,我只是引用了一个例子,与 x86 相比,power pc 是大端
                    • @dwc: 不同平台上的相同功能。
                    猜你喜欢
                    • 1970-01-01
                    • 2010-09-27
                    • 2011-01-03
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2010-09-05
                    • 2014-11-28
                    相关资源
                    最近更新 更多