In the meantime I downloaded the Abbrevia package from SourceForge, and came across several unpleasent constructs. Please let me introduce my preferred programming model for portable code.
1) Target Dependencies
I don't accept any OS or machine specific conditional compilation in code, except in dedicated units. In Abbrevia the same fixes occur in various places in the code, instead of in a dedicated OS specific procedure. Such a procedure exists, but unfortunately only in the unpatched form :-(
Also compiler specific conditionals should be used only where absolutely necessary (local options, syntax differences...).
2) Code Checks
It should be possible to check code for compilation on other platforms. This requires that target specific code can be compiled on any host, somehow. Consequently it must be possible to override the compiler generated symbols that describe the environment.
3) Standard Units
Portable code shall use standard procedures and units for target specific actions. This requires the definition, implementation, and consequential use of one or more standard units for:
- Machine specific data types and procedures (endianness...)
- OS specific data types and procedures
- C and POSIX specific data types (for code ported from GNU/C) Then it should be clear which portable data types and procedures should be taken from which unit. It also should be easier to determine which procedures need special considerations in portable code, from the defined data types and procedures in these units.
It's not necessary that everybody follows these principles, but I will do so in my own published portable code. Otherwise I can continue to write my code for Delphi, and let it to somebody else to port that code to e.g. FPC.
In the simplest case these units will have an unconditional interface part, the target specific data type declarations and the implementation part can be included conditionally from the appropriate locations. For testing purposes fake units or code sections can contain the OS specific procedure declarations, with empty bodies. This way I could verify that my changes to the main stream code would not break the code for other environments, due to the use of not yet ported constants or data types etc.
This model has one disadvantage, in so far as complex procedures with small embedded platform specific code could no more be checked, because it often was impossible to determine the impact of changes to local variables etc. in the conditionally excluded code. It also was and is impossible to automatically verify that the conditional sections really are implemented for every target!
In another implementation of a target independent library I defined portable classes with standardized methods. These virtual methods were almost abstract in the base class, so that they had to be overridden in every derived class. In the initialization section of the unit the appropriate class was created and assigned to the according variable, based on the actual or desired target environment. Of course this approach has the disadvantage of increasing runtime (how much?), due to the virtual method calls. Nonetheless I think that in many cases the difference from the traditional method will not be noticeable, due to the (unknown, long?) runtime spent in the embedded OS calls.
The best compromise might be a mix of both methods. The interface can be fully procedural, and objects with virtual methods are used only in the implementation part, and only when this approach makes sense. Perhaps somebody finds another way to achieve the same goal, with less disadvantages?
I'm willing to demonstrate my ideas in a redesign and extension of Abbrevia, so that we have a concreter base for further discussions. But before starting with that work I would like to hear some encouraging <g> opinions or suggestions.
DoDi
