[Baidu Space] [Original] cross-platform programming Considerations (III): window to Android porting

Source: Internet
Author: User
Tags posix

The big question

Take a look at the general direction of cross-platform attention first.

1.OS and CPU

The same operating system, the CPU may also be different, such as Windows has an ARM CPU-based version, and Android currently has several x86,arm,mips.

Even with the same CPU architecture family, the details are different.

So at present, the individual prepared 3 macro switches to determine the target platform. OS, CPU, cpubits

OS for different operating systems, CPU corresponding to different CPU architectures (such as x86 and arm), Cpu-bits is currently 32 and 64, such as the CPU is x86, Cpubits is 64, then corresponding x86_64 (x64) platform. The same ARM series also has 32-bit and 64-bit differences.

32/64-bit processing has been reproduced before an article specifically stated, here is not much to say.
The final target platform can be determined by these three macros. But in order to reduce maintenance costs, these macros are generally only used in the underlying package, in the upper or less used as good, can not use as much as possible, as far as possible using the standard C + +.

SIMD, Intel Atom supports SSE, and arm has a neon instruction set, which can refer to the NDK sample.

2. Program data.

In general, it is necessary to generate one copy of the corresponding data based on each target platform.

The advantage of this is that each platform's data can be adjusted according to the target characteristics. For example, the mobile platform's scene size/quality and data volume are generally relatively small.

Because the data processing characteristics of each CPU (cache line, alignment, SIMD instruction set) may be different, special handling is required.

For the format of the data, there are two approaches, the first is that each platform's packet format is different, the second is the use of a unified format, that is, although each target platform has a separate packet (due to the target size), but theoretically a platform can load all other platform's packets.

The first is different, the data format of each platform is different, such as the member alignment of a struct, then the serialized data will have the difference between size and offset.

The first approach does not have to specifically consider data alignment and so on, but to generate and maintain packages and tools for different target platforms, although maintenance costs are relatively low, it can be cumbersome if there are many tools.

The second way to consider the alignment in the code and so on, to ensure that a packet can be loaded by all platforms, it may be cumbersome to do so, need to be particularly careful, and complete data testing.

Here is a processing of the structure alignment of Android under x86 and arm (4.1 forced Memory Alignment):

Http://software.intel.com/en-us/articles/android-application-development-and-optimization-on-the-intel-atom-platform

Reference Http://software.intel.com/en-us/blogs/2011/08/18/understanding-x86-vs-arm-memory-alignment-on-android/

For example, the "-malign-double" gcc parameter used in the first link above enforces a 8-byte boundary of 8 bytes of data alignment. Ensure that the same data can be loaded by different CPUs. The second link uses __attribute__ ((aligned (n))) to force the alignment of the data members.

My company is using the first way. The problem with the first approach is that the same OS has different target CPUs (platform), so the x86 has one copy of the data for the Android platform, and arm has another piece of data. So personal preference in the second way, in addition to the above link in the way, you can also force do not read and write the struct, only read and write atomic data, a struct member read and write individually.

--Attached:

Given the endian problem, general data writing uses a fixed endian, which makes a byte swap based on the target characteristics when reading. It should not be difficult to encapsulate the reading and writing of atomic data.

3.Platform APIs and libraries

Different OS platform APIs vary, but fortunately most OSes support the POSIX API interface specification, so in general, if the features that are not supported by standard C + +, you can use the POSIX interface as much as possible. The most personal encounters are those of Io, thread and timer (timer).

Although Windows also supports POSIX subsets, some functions do not, this time use the OS macro to do conditional branching, using the Windows API.

For example, under Windows FindFirst traversal file, under Android/posix to use Opendir.

However, in order to facilitate the transplant and maintenance, pay attention to a principle, try not to expose the platform header: The public header does not refer to the system header file, which is a mandatory error-checking mechanism. Because once exposed, the upper-level code is easily misused. For example, the game code inside the use of handle, Win32 under the compiler no problem, so no errors found, the other platform will be wrong. If necessary, use macros to separate platform-related data.

For some cross-platform functions, if you do not want to write their own, there are a lot of out-of-the-box cross-platform three-way library can be used, they have to encapsulate the common platform, skipping the tedious native API conditional compilation, such as the UI system can use the WX/QT library and so on.

threads, most OSes support pthread or a subset of them, since the author uses Intel TBB, and the latest tbb4.x already supports the Android platform, updating TBB3.0 to the latest version and compiling is available shortly before. There's not much to say here.

4. Compiler

Compiler preferred GCC, cross-compiled artifact Ah. But because of the historical reasons (the author started using MSVC, the project from VC8 to VC11, currently retains VC10 and VC11), here also introduced MSVC, and Jane said both.

For c++11, GCC supports better. For C, msvc basically stops at C89. If you want to consider as many compiler support as possible, use c++03 and C89 as much as you can.

For Clang, has not been used, is not discussed temporarily.

Different compilers have different extensions, and many extensions are equivalent. If the extension format is similar, then it can be written in the base header file. Like Ogre's alignment, I took it with me:

1234567891011 #if defined(_MSC_VER)#   define BLADE_COMPILER BLADE_COMPILER_MSVC#   define BLADE_ALIGNED(n) __declspec(align(n))#   define BLADE_FUNCTION   __FUNCTION__#elif defined(__GNUC__)#   define BLADE_ALIGNED(n)   __attribute__((aligned(n)))#   define BLADE_COMPILER BLADE_COMPILER_GNUC#   define BLADE_FUNCTION   __PRETTY_FUNCTION__#else#   error "Compiler not supported yet."

#endif

This allows the struct blade_aligned (8) yourstruct {} to be used for a structure that needs to be aligned. To specify alignment, calling Convension is similar, but I only have a definition here, not actually used.

For a different format of the extension (some compiler Intrinsics), can be used in the final use of the compiler switch to conditional compilation, you can also wrap the basic function as a basic class, in the implementation of the same use of conditional compilation, so that the upper-level use of code is more uniform.

By the way, my company's tool chain, he has his own make system, such as Xmake, has his own compiler front-end collection, such as XCC, using non-standard C language.

Through an XC Compiler, the custom syntax and the extended C language are converted to the cost of the compiler (VC/GCC) supported by the source code, and then compiled using the local compiler.

The advantage of this is that cross-platform/compiler things can be all in their own compiler extension inside, the upper code is very clean, as long as the syntax to write, then write out the code is cross-platform (in addition to the platform API requires a separate encapsulation), Basically, you don't have to think about compiler and CPU-related stuff, and it's mandatory and rarely goes wrong.

The disadvantage is the cost of learning, a standard C + + programmer needs a certain amount of time to adapt to his grammar.

Of course, he basically does not support C + + (except for class/dynamic binding, templates and STL are not supported), so a bit disgusting, but you can directly call the local compiler, compiled standard C + + code, and most of its ABI-compatible, can be linked. So you can compile these code and links like MFC, but there are some limitations.

There are also historical reasons, because this set of tools is very early, and that time may be the first C standard has not come out. That's why the dues are very strong, but now it's stable. And he was very early in support of some extensions that the current c++11 had just had. Overall is a good thing, but if it is now, there is a mature compiler toolchain and language standards, it is estimated that no one will go on doing so.

Details

It then records the details of the migration process, mainly the problems encountered in the process of msvc to GCC.

The problem is that MSVC support for standards is poor, allowing many dialects that are not allowed by standards.

Record these details and develop good programming habits later on.

There is also a part of the Android OS API and Bionic libc issues.

1. Templates

When nesting templates, the standard does not allow <<>> this way, because it conflicts with operator>>. VC does not have this problem. So the correct way is to use a space to separate, this very early done, only part of the attention of the change over.

12 typedefstd::vector<std::vector<int>> intdvector; //errortypedefstd::vector< std::vector<int> > intdvector;

Also in the cast, it is best to use a space to separate <>, to avoid compilation errors,

12 ::RGBQUAD& quad = reinterpret_cast<::RGBQUAD&>( q );    //error::RGBQUAD& quad = reinterpret_cast< ::RGBQUAD& >( q );

2. Type conversions for composite keywords

If it is a single keyword, it is legal. But MSVC allows

1 unsigned shorts = unsigned short(1.0f);

Such a conversion, but GCC, for example, would like to expand the combination of keywords with parentheses (as to which is the standard, not to check, but the conjecture should be GCC)

1 unsigned shorts = (unsigned short)(1.0f);

3.mutable Reference

12345 structA {...};struct B{    mutableA& mARef; //error};

GCC does not allow the use of mutable reference because reference can be modified within the const member function.

This is not to check the standard, but should be tacit.

4. Pure virtual Destruction

MSVC allows the pure virtual destructor (the implementation of pure virtual functions) to be placed inside the class, but the standard does not allow it. This long ago checked the standard, and changed the code, but then a little bit of code because of the habit of the reason and so write.

123 virtual~Class() = 0 {} // OK on MSVC, error on GCCvirtual ~Class() = 0; //OKvirtual~Class() {} //OK

5.Function Scope functor

12345678 iterator findSomeThing(){    structFnFinder    {        bool operator(const T& val) { ... }    };    returnstd::find_if( v.begin(), v.end(), FnFinder() );}

This compilation error under GCC requires that the struct fnfinder be defined outside the function body.

6. Template export for dynamic libraries

The front is a small change, but this is the most headache problem.

The DLL export template under Msvc is simple:

Dll_export_api is based on the current compiled code, is the local DLL code, or the client code that references the DLL __declspec (dllexport)/__declspec (dllimport)

The local code is the code that compiles the DLL, and the client code refers to the module code that uses the DLL (the corresponding header file) and links the DLL.

12345 #   ifdef DLL_LOCAL_COMPILE#   define DLL_EXPORT_API __declspec( dllexport )#   else#   define DLL_EXPORT_API __declspec( dllimport )#   endif

Then declare the export template in the common header file:

1 templateclassDLL_EXPORT_API Handle<Class>;

A unique class code can be generated within the DLL as long as any of the compilation units inside the local DLL contain the above statement (which can be included repeatedly).

But GCC has a different mechanism: because there is no special export keyword, it only controls visibility with visibility: (all symbols are not visible when set to default)

12 #DLL_EXPORT_API __attribute__ ((visibility("default")))templateclassDLL_EXPORT_API Handle<Class>;

This explicit instantiation of the statement generates a instantiation code in each compilation unit of the client code. Instead of generating a reference to an external link based on __declspec (dllimport), as MSVC does.

If it is a pure code class, the most is to generate redundant code, the target file is too large. This may barely be overlooked.

However, the problem is that static variables inside class or static variables inside member functions are also multiple, resulting in multiple instances of a single pointer to some classes, such as Singleton, resulting in liba.so The singleton instance is not the same instance as the singleton instance of libb.so.

The template under GCC supports declaring an external template.

1 externtemplate classHandle<Class>;


This prevents the template from being instantiated when the client code uses the declaration, giving him a reference to the external link (. So export symbol).

The actual display instantiation is placed in a compilation unit of the Local. So file and cannot be declared repeatedly (repeated subsequent instantiation, resulting in duplicate symbols, link errors).

The final version (GCC & MSVC) is as follows:

12345678910 #   if Blade_compiler = = Blade_comp Iler_msvc #       ifdef xxx_dll_local_compile #            define DLL_EXPORT_API __declspec (dllexport) #        Else #            Define DLL_EXPORT_API __declspec (dllimport) #       endif #   elif Blade_compiler = = Blade_compiler_gnuc #       define DLL_EXPORT_API __attribute__ ((Visibility ("default")) #   endif   extern   template   class   Dll_export_api handle< Class>

Although MSVC warns that __declspec (dllexport) and extern are incompatible (because dllexport is required to generate instantiated code locally as an export symbol, it conflicts with the meaning of extern) but this warning can be turned off.

At the same time, add a unique sentence to a compilation unit in the local library:

1 templateclassHandle<Class>;

To complete the local template instantiation of GCC, and for msvc, there is no such sentence, because the above extern template class Dll_export_api handle<class>; The instantiation of the export template has been completed (extern is ignored) as long as it is contained by any one or more local compilation units.

The final porting effort is to add an extern keyword to all the export template declarations and a corresponding display instantiation declaration for a compilation unit on the local. So project. At the moment it feels like the change is minimal.

--attached
It is also important to note that if a template class A is integrated from another template class B, then the extern template class is valid only for this class, and the base class does not have an extern property. Fortunately, I don't have a lot of things here. However, it is necessary to add the base class for each extern explicit instantiation, because it is a bit cumbersome, according to the current situation, directly in the subclass overwrite out the parent class function, in the subclass of the export function to call the parent class function, avoid calling the parent class function directly causes the parent class to be instantiated in the client code, this can also be resolved.

Wide characters are not supported for libstdc++ under 7.Bionic libc

It is said that the previous version of Sizoeof (wchar_t) was 1, later supported, is 4, but the current version (NDK R9) wide character library function is still not available.

such as vswscanf and so on, this also need to own black.

No MessageBox under 8.NDK

This function must be used because the MessageBox is made into an underlying framework function.
But there is almost no window API under the NDK, which needs to be addressed by using JNI, which is called Java in the C + + layer.

Originally used the NDK built-in nativeactivity want to skip Java coding, write full native code, it does not seem to be OK at the moment.

9.dlopen does not support Rtld_nodelete

Due to the use of a lot of dynamic libraries, and in order to debug memory, the upper library internal buffer pointer such as __FILIE__, __pretty_function__ to the base library, in memory leak dump the need to put these upper library closed ( After all objects are released), the dump is not possible.

But after these top-level libraries are unloaded, these localbuffer have been released with the release of. So. Because these constant buffer is usually the constant section generated directly inside the. So image at compile time. This causes leak dump to reference an invalid wild pointer.

There are similar problems under Windows, but the Getmodulehandleex Get_module_handle_ex_flag_pin under Windows can support the permanent non-unloading of libraries, equivalent to Rtld_nodelete, In debug mode need to open in order to normal leak dump, otherwise there may be leaks when the crash.

This question has been submitted to the official issue: http://code.google.com/p/android/issues/detail?id=64069

10.GLES 2.0

This part is not realized, save the later supplement. In addition to Gles, all the other modules have been ported and the real-machine test can start running.

The first simple summary of the rendering of the section, the main rendering equipment (Drawcall/render state), Texture/rendertarget, Index/vertexbuffer, Shader, these packages, are physical activity, there are many pits. But the tone of the transplant has been set, and these are available later in the work, it is best to wait until the GLES3.0 popularized. Behind the main task is platform-independent graphics effects, such as the effect is almost done, and then the transplant can be used.

11. Other

There are many apk packaging restrictions, such as Google Play on the requirements of the APK maximum 50M, more than the data need to use expansion pack, APK packaging time file name/directory structure restrictions, dynamic library. So load limits and so on, have been recorded in the previous note.

[Baidu Space] [Original] cross-platform programming Considerations (III): window to Android porting

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.