- 论坛徽章:
- 0
|
Spell-check your documentation, README files and error messages in your software. Sloppy code, code that
produces warning messages when compiled, and spelling errors in README files or error messages, leads users to
believe the engineering behind it is also haphazard and sloppy.
Recommended C/C++ portability practices
If you are writing C, feel free to use the full ANSI features. Specifically, do use function prototypes, which will
help you spot cross-module inconsistencies. The old-style K&R compilers are ancient history.
Do not assume compiler-specific features such as the GCC "-pipe" option or nested functions are available. These
will come around and bite you the second somebody ports to a non-Linux, non-GCC system.
Code required for portability should be isolated to a single area and a single set of source files (for example, an
"os" subdirectory). Compiler, library and operating system interfaces with portability issues should be abstracted to
files in this directory. This includes variables such as "errno", library interfaces such as "malloc", and operating
system interfaces such as "mmap".
Portability layers make it easier to do new software ports. It is often the case that no member of the development
team knows the porting platform (for example, there are literally hundreds of different embedded operating
systems, and nobody knows any significant fraction of them). By creating a separate portability layer it is possible
for someone who knows the port platform to port your software without having to understand it.
Portability layers simplify applications. Software rarely needs the full functionality of more complex system calls
such as mmap or stat, and programmers commonly configure such complex interfaces incorrectly. A portability
layer with abstracted interfaces ("__file_exists" instead of a call to stat) allows you to export only the limited,
necessary functionality from the system, simplifying the code in your application.
Always write your portability layer to select based on a feature, never based on a platform. Trying to create a
separate portability layer for each supported platform results in a multiple update problem maintenance nightmare.
A "platform" is always selected on at least two axes: the compiler and the library/operating system release. In some
cases there are three axes, as when Linux vendors select a C library independently of the operating system release.
With M vendors, N compilers and O operating system releases, the number of "platforms" quickly scales out of
reach of any but the largest development teams. By using language and systems standards such as ANSI and
POSIX 1003.1, the set of features is relatively constrained.
Portability choices can be made along either lines of code or compiled files. It doesn't make a difference if you
select alternate lines of code on a platform, or one of a few different files. A rule of thumb is to move portability
code for different platforms into separate files when the implementations diverged significantly (shared memory
mapping on Unix vs. Windows), and leave portability code in a single file when the differences are minimal (using
gettimeofday, clock_gettime, ftime or time to find out the current time-of-day).
Avoid using complex types such as "off_t" and "size_t". They vary in size from system to system, especially on 64-
bit systems. Limit your usage of "off_t" to the portability layer, and your usage of "size_t" to mean only the length
of a string in memory, and nothing else.
Never step on the namespace of any other part of the system, (including file names, error return values and
function names). Where the namespace is shared, document the portion of the namespace that you use. |
|