Overview
Talk about the portability of C + + today. If you normally use C + + for development, and you are not very clear about the portability of C + +, then I suggest you look at this series. Even if you don't have the need for cross-platform development at the moment, it's helpful to know about portability.
C + + Portability This topic is very large, including compilers, operating systems, hardware systems and many other aspects, each has a lot of content. In view of my ability and energy are limited, can only introduce each one of the most easily encountered problems for the reference of the guys.
Later I will be from the compiler, C + + syntax, operating system, third-party libraries, auxiliary tools, development process and other aspects of the introduction.
Compiler
In the cross-platform development process, many problems are related to the compiler. So let's talk about compiler-related issues first.
Compiler's Choice
First, GCC is a priority to consider, because almost all operating system platforms have GCC available. It's basically a generic compiler. If your code in the A platform GCC can be compiled, and then get the B platform with a similar version of the GCC compiled, generally not too much problem. So GCC is sure to consider the support.
Second, consider whether to support the local compiler. The so-called local compiler is the operating system manufacturer's own compiler. For example, the local compiler relative to Windows is Visual C + +. The local compiler relative to Solaris is Sun's CC. If you are sensitive to performance or want to use some of the advanced features of a local compiler, you might consider supporting the local compiler while supporting GCC.
Compile warning
The compiler is a programmer's friend, a lot of potential problems (including portability), the compiler can be found and give a warning, if you usually pay attention to these warning messages, can reduce a lot of trouble. Therefore, I strongly recommend that:
1 increase the warning level of the compiler;
2 do not easily ignore compiler warning messages.
Cross compiler
See "Wikipedia" for the definition of cross-compiler. In layman's words, a binary program running on platform B is compiled on platform A. Let's say that the application you're developing is running on Solaris, but you don't have a sparc machine on hand to run Solaris, and the cross-compiler comes in handy. In general, GCC is used to make a cross-compiler, which is limited to space, and this is not a very deep chat. Interested students can refer to "here".
Exception handling
Previous post "syntax" because space is limited, there is no time to talk about the anomaly, and now the exception-related parts of the separate to take a look.
Beware of new allocation memory failure
Early old compiler-generated code, if new fails, returns a null pointer. I used to Borland C + + 3.1 seems to be the case, now this compiler should be rare. If the compiler you're using now has this kind of behavior, you're going to be miserable. You can consider overloading the new operator to throw a Bad_alloc exception to facilitate exception handling.
A slightly more modern compiler would not simply return a null pointer. When the new operator finds a memory emergency, according to the standard (see C + + 03 standard 18.4.2 section), it should go to call the New_handler function (prototype for typedef void (*new_handler) ();). Standard recommended New_handler functions do the following three things:
1. Try to get more memory.
2, throw bad_alloc abnormal;
3. Call abort () or exit () to exit the process.
Since the New_handler function can be reset (by calling Set_new_handler), it is possible to have the above behavior.
In summary, new allocates memory fails, there are three possible possibilities:
1, return null pointer;
2, throws the exception;
3, the process immediately terminates.
If you want your code to be well-ported, you have to take these three scenarios into account.
Caution With exception specifications
Exception specifications In my opinion is not a good thing, do not believe can go to see "C + + Coding Standards-101 rules, guidelines & best Practices" 75th. (specifically what is bad after a special C + + exception and error handling post to chat), according to the standard (see 03 standard 18.6.2), if a function thrown outside the exception is not included in the function's exception specification, then should call Unexcepted (). However, not all compiler-generated code adheres to standards (such as some versions of the VC compiler). If the compiler you need to support does not behave consistently on the exception specification, consider removing the exception specification declaration.
Do not throw exceptions across modules
The module mentioned here refers to the dynamic library. If your program contains multiple dynamic libraries, do not throw exceptions outside of the module's export function. After all, C + + does not have the ABI standard (presumably not in the future), and it can be unpredictable to throw exceptions across modules.
Do not use structured exception handling (SEH)
If you have never heard of SEH, then let me skip the paragraph if I don't say it. If you used to use SEH before, get rid of the habit before you try to write cross-platform code. Code containing SEH can only be compiled on the Windows platform and certainly not cross-platform.
About catch (...)
It is supposed that catch (...) The statement can only capture C + + exception types and is powerless for access violations, except for non-C + + exceptions such as 0 errors. However, in some cases (such as some VC compilers), such as access violations, except 0 errors can also be caught (...) Capture. So, if you want the code to be good, you can't rely on the catch (...) in the program logic. 's behavior.
Hardware system related
The topic of this conversation is mainly related to the hardware system. For example, your program needs to support different types of CPUs (x86, SPARC, PowerPC), or CPUs with different lengths of the same type (such as x86 and x86-64), you need to be concerned about the hardware system.
The size of the base type
The size of the base type in C + + (the number of bytes consumed) varies with the CPU word length. So, if you want to represent an int occupies a number of bytes, do not directly write "4" (by the way, directly write "4" also made magic number of the big bogey, see here), and should write "sizeof (int)"; If you want to define a signed integer size that must be 4 bytes, do not use int directly, use pre-typedef good fixed-length type (such as int32_t of Boost library, ace_int32 of ACE Library, etc.).
Almost forgot, the size of the pointer also has the above problem, also be careful.
byte order
If you haven't heard of the "byte-order" thing, see "Wikipedia." In a popular analogy, there is a 4-byte integer 0x01020304 on a large-tailed machine, and a machine that passes through a network or file to a small-tailed sequence becomes 0x04030201; it is said that there is a medium-tailed machine (but I have not touched it), The above integers will become 0x02010403.
If you are writing an application that involves network communication, be sure to make a translation of the host order and the network sequence, and remember to do a similar conversion if you are involved in transferring binary files across machines.
Memory alignment
If you don't know what "memory alignment" is, see "Wikipedia." To put it simply, the data in the struct is not next to each other for performance considerations on CPU processing, but there are some gaps to open. In this case, the address of each data in the struct is exactly the integer multiple of a word length.
Because the C + + standard does not define the details of memory alignment, your code cannot rely on the alignment details. Wherever the size of the structure is calculated, the sizeof () is faithfully written.
Some compilers support #pragma pack preprocessing statements (which can be used to modify the Chi), but this syntax is not supported by all compilers and should be used with caution.
Shift operation
For right-shift operation of signed integers, some systems use arithmetic to move right (the highest sign bit is unchanged) by default, and some use logical right shift (the highest sign bit is 0). Therefore, do not move the signed integer to the right. By the way, even without the portability problem, the shift operators are used sparingly in the code. Those who attempt to improve performance with shift operations should pay more attention to the fact that doing so is not only very readable, but also thankless. As long as the less retarded compiler, will automatically help you to solve this optimization, no need to worry about the programmer.
Operating system
The previous post referred to the topic "Hardware architecture," and today it is about operating system-related topics. C + + cross-platform development and OS-related trivia very much, so today will be long-winded, please yours faithfully crossing forgive:-)
In order not to bypass the mouth, the following Linux and a variety of Unix collectively known as POSIX system.
File System (filesystem hereinafter referred to as FS)
New to the beginning of cross-platform development, most of the novice will encounter issues related to the FS. So let's talk about FS first. summed up, the development of the easy to meet the FS differences are mainly as follows: Directory delimiter differences, case-sensitive differences, the difference between disabled characters in the path.
To address the above differences, you should pay attention to the following points:
1, file and directory naming to standardize
When naming files and directories, try to use only letters and numbers. Do not delegate two files with similar names in the same directory (names differ only in case, such as Foo.cpp and Foo.cpp). Do not use certain OS reserved words (such as aux, con, nul, PRN) as filenames or directory names.
To add, the naming that you just said includes source code files, binaries, and other files created at run time.
2. #include语句要规范
When you write an # include statement, be careful to use a forward slash "/" (more generic) instead of a backslash "\" (Available only in Windows). #include语句中的文件和目录名要和实际名称保持大小写完全一致.
3, the code involves the FS operation, try to use the ready-made library
There are already a lot of mature third-party libraries (such as Boost::filesystem) for FS. If your code involves FS operations (such as directory traversal), using these third-party libraries as much as possible can help save you a lot of things.
★ Text file return cr/newline LF
This annoying problem is caused by the inconsistent handling of carriage return/line feeds by several well-known operating systems. The current situation is: Windows uses both CR and Lf;linux and most of UNIX uses LF; Apple's Mac family uses CR.
For source control, fortunately many version management software (such as CVS, SVN) intelligently handles this problem, allowing you to retrieve the local source code from the codebase to fit the local format.
If your program needs to work with text files at run time, be aware of the differences in how this article opens and binary. In addition, if you are involved in transferring text files across different systems, consider appropriate processing.
★ File search path (including search executables and dynamic libraries)
Under Windows, if you want to execute a file or load a dynamic library, the current directory is typically searched, and the POSIX system is not. So if your app involves starting a process or loading a dynamic library, be careful about the difference.
★ Environment Variables
For the above mentioned search path problem, some students want to modify path and Ld_library_path to introduce the current path. If you use this method, it is recommended that you only modify the process-level environment variables, do not modify the system-level environment variables (modify the system level may affect the same machine other software, produce side effects).
★ Dynamic Library
If your application uses dynamic libraries, it is strongly recommended that the dynamic Library export standard C-style functions (try not to export classes). If you are loading a dynamic library in a POSIX system, remember to use the RTLD_GLOBAL flag bit with caution. This flag bit will enable the global symbol table, which may cause the symbol name conflict between multiple dynamic libraries (once this happens, there will be a bizarre run-time error, which is extremely difficult to debug).
★ Service/Caretaker Process
If you don't know the concept of the service and guarding process, see Wikipedia (here and here). For the sake of narrative convenience, the following collectively referred to services.
Since the modules developed by C + + are mostly back-end modules, they often encounter service problems. Writing a service requires invoking several system-related APIs, resulting in tight coupling with the operating system, which is difficult to do with a set of code. So a better approach is to abstract a generic service shell and then mount the business logic code as a dynamic library underneath it. In this case, at least the code that guarantees the business logic requires only one set; the code for the service shell requires two sets (one for Windows, one for POSIX), but they are business-agnostic and can be easily reused.
★ Default Stack size
Different operating systems, the default size of the stack varies greatly from dozens of KB (it is said that Symbian is only 12K, really stingy) to a few megabytes. So you have to inquire about the default stack size of the target system, and if it happens to be stingy like Symbian, consider using the compiler option to scale up. Of course, it is important to develop a good habit of "not defining large arrays/large objects on the stack", otherwise the large stacks will be blown up.
Multithreading
The last one months to write a more miscellaneous posts, resulting in this series has not been updated for a long time. As a result, there are netizens in the comments urging me, make me a bit embarrassed. Today, the multi-threaded article to fill up. Last time I talked about the operating system, assorted said a lot because of the trivial issues related to OS. At that time, a little bit longer, we left the multi-process and multi-threaded parts to the back.
★ Compiler
◇ about C Runtime Library options
First, a very basic question: about the settings of the C Runtime Library (hereafter referred to as Crt:c run-time). Originally did not want to talk about such a low-level problem, but there are several people around this place to eat a loss, so still talk about.
Most C + + compilers will come with a CRT (possibly more than one). Some compilers come with a CRT that can be divided into single-threaded CRT and multithreaded CRT based on thread support. When you want to do multi-threaded development, don't forget to make sure that the relevant C + + project uses a multithreaded CRT. Otherwise it would be very hard to see.
Especially when you use Visual C + + to create projects, be more careful. If the new project is MFC-free (including console engineering and WIN32 Engineering), the default setting for the project is to use the single-threaded CRT, as shown in:
◇ About optimization Options
"Optimization Options" is another key compiler-related topic. Some compilers offer an optimization option known as Bull X, but some optimization options can be potentially risky. The compiler may have taken the liberty of disrupting the order of execution instructions, leading to unexpected line Cheng (Race Condition, detailed explanation of "here"). Liu Weipeng students in the "C + + multithreaded memory model" to give a few typical examples, you can go to see.
It is recommended that you only use the compiler General speed optimization option. Other fancy optimization options may not be obvious, but the potential risks are not small. It's really not worth the risk.
Take GCC For example: it is recommended to use the-O2 option (in fact-o2 is a collection of options), there is no need to risk-o3 (unless you have a good reason). In addition to-o2 and-o3, GCC also has a large (estimated hundreds) of other optimization options. If you try to use one of these options, be sure to first clarify its characteristics and possible side effects, otherwise you will not know how to die in the future.
★ Line Libraries Selection
Because the current C + + 03 standard has little to do with thread-related content (even if the C + + 0x contains a standard library of threads in the future, the support of the compiler vendor may not be comprehensive in the short term), cross-platform multithreading support is still dependent on third-party libraries for a long time to come. So the choice of line libraries is much more important. The following is a general introduction to several well-known cross-platform line libraries.
◇ace
Let's start with the ace, the historic library. If you have never touched it before, watch "here" for literacy. Judging from the full name of ACE (Adaptive Communication Environment), it should be based on "communication". However, ACE's support for the "multithreading" side of the sideline is very comprehensive, such as mutexes (Ace_mutex), condition variables (ace_condition), semaphores (Ace_semaphore), Fences (ace_barrier), Atomic operations (Ace_ ATOMIC_OP) and so on. Some types such as Ace_mutex are also subdivided into thread read and write locks (ace_rw_thread_mutex), Thread recursion locks (Ace_recursive_thread_mutex), and so on.
In addition to support is very comprehensive, Ace has another obvious advantage is that the various operating system platform and its own compiler support is very good. Including some old-fashioned compilers (such as VC6), it is also able to support (the support described here, not only to compile through, but also to be able to run stably). This advantage is quite noticeable for cross-platform development.
The downside? Since Ace started early (probably in the middle of the 90 's), many of the old C + + features haven't come out yet (let alone new features), so it feels like the whole style of ACE is older than boost.
◇boost::thread
The boost::thread is in stark contrast to ace. This thing seems to have been introduced from the boost 1.32 release, which is shorter than ace. But thanks to the support of a bunch of Daniel in boost, the development is pretty fast. To the current boost 1.38 version, you can also support many features (though there seems to be no ace). Given the many members of the C + + Standards committee gathered in the boost community, Boost::thread will eventually become the star of tomorrow for C + + threads over time.
The disadvantage of boost::thread is that there are not enough compilers to support, especially some older compilers (many boost subpackage have this problem, mostly because of the advanced template syntax). This is a more obvious problem for cross-platform.
◇wxwidgets and QT
Both Wxwidgets and Qt are GUI libraries, but they are also built-in and support for threading. Wxwidgets Thread Introduction can see "Here", about the QT thread introduction can see "here". The two libraries have similar support for threads, and provide common mechanisms such as mutexes, condition, semaphore, and so on. However, the feature does not have ace enrichment.
◇ How to weigh
For the development of GUI software and already used wxwidgets or QT, you can use their built-in line libraries (if you only use basic threading function). Due to their built-in line libraries, the features are slightly too thin. In case you need an advanced threading feature, consider replacing it with Boost::thread or ACE.
As for Boost::thread and Ace's trade-offs, the main need to see the software needs. If you want to support the platform is quite miscellaneous, it is recommended to choose Ace, so as not to encounter the compiler does not support the problem. If you only need to support a few mainstream platforms (such as Windows, Linux, MAC), it is recommended to use Boost::thread. After all, the compiler on the main operating system, support for boost is fine.
★ Programming Considerations
In fact, multi-threaded development, need to pay attention to a lot of places, I can only approximate a few impressions of the more deep attention matters.
◇ about volatile
When it comes to the pitfalls that multithreading can encounter, you have to mention the volatile keyword. If you don't know anything about it, first look at "here" to get a bit of literacy. Because both the C + + 98 and the C + + 03 Standard do not have a multithreaded memory model defined, the standard also has a bit of edge on volatile and threading. As a result, quite a bit of saliva in the C + + community is concentrated on volatile (many of which have C + + Daniel's saliva). In view of this, I will not be more verbose here. A few of Daniel's articles are recommended: Andrei Alexandrescu's article "Here", and Hans Boehm's article "Here" and "here". Let's go and have a look.
◇ about atomic operation
Some students just know that multiple threads of competition write need to lock, but do not know that multiple read single write also need to protect. For example, there is an integer int ncount = 0x01020304; In the concurrency state, a write thread modifies its value ncount = 0x05060708; another read thread gets the value. Is it possible for a read thread to read a "bad" (such as 0x05060304) data pinch?
Whether the data is broken, depends on whether the read and write to the ncount are atomic operations. This relies on a number of hardware-related factors (including the type of CPU, the length of the CPU, the number of memory-aligned bytes, and so on). In some cases, it is true that data may be broken.
Since we're talking about cross-platform development, God knows what kind of hardware environment your code will be performing in the future. So when dealing with a similar problem, use the atomic operation classes/functions provided by the third-party library (such as Ace's Atomic_op) to ensure security.
◇ on the destruction of objects
In the previous series of posts, "How did C + + objects die?" "Inside, has introduced the Win32 platform and the POSIX platform the non-natural death problem of the thread."
Since the above-mentioned cross-platform line libraries is to invoke the threading API that comes with the operating system, everyone should try to make sure that all threads are naturally dead.
C + + portability and cross-platform development