From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,a9f89faeb8f41ad0 X-Google-Attributes: gid103376,public From: nabbasi@earthlink.net Subject: Re: ifdef replacement for GNAT Date: 1998/04/11 Message-ID: <6gn4q1$ee6@drn.newsguy.com> X-Deja-AN: 343012649 References: <352287EE.1CFB@tolstoy.mdc.com> <352B8208.41C6@tolstoy.mdc.com> <86g1jm2gcf.fsf@zappa> Organization: Newsguy News Service [http://www.newsguy.com] Newsgroups: comp.lang.ada Date: 1998-04-11T00:00:00+00:00 List-Id: In article , dewar@merv.cs.nyu.edu says... >The proper approach for achieving target dependence is to follow two steps > > (a) encapsulte the target dependence down to the minimum level > > (b) Provide separate target dependent units for these remaining functions > Prof. Dewar, let me try to see what you mean by point (a) above by an example (I am also one of those who learn better by examples, for no other reason other than to be clear on what things mean): Lets assume we have UNIX and VMS (for the purpose of this example). Lets assume one wants to create a child process. on UNIX one calls fork(), on VMS one calls vfork(). There are nowadays 3 main methods of how to do this. first solution ============= cc -DUNIX program.c program.c +--------------------------------------+ | #if defined(VMS) | | status = vfork() | | #else | | #if defined(UNIX) | | status= fork() | | #else | | #error "unsupported platform" | | #endif /* UNIX */ | | #endif /* VMS */ | +--------------------------------------+ Now, I do not like the above myself, for the same reason you say, it also makes the code hard to read and maintain. second solution ================ The other approach is to do this: cc -DUNIX program.c program.c +-----------------------------+ |#include "program.h" | | status= GENERIC_fork(); | +-----------------------------+ program.h +---------------------------------+ | #if defined(VMS) | | #define GENERIC_fork vfork | | #else | | #if defined(UNIX) | | #define GENERIC_fork fork | | #else | | #error "unsupported system" | | #endif | | #endif | +---------------------------------+ This above is a little better since the main source code is not cluttered, and the ifdef stuff is kept in one header file, but it also suffer from some of the same problems. It also has an additional problem which is against the "what you see is what you get". i.e. one looks at the source code in program.c, they do not know actually what the call will turn up to be without looking around in some other header files to find out. (another variation on the above is to use a compiler directive (as in -Dfork=vfork) to make replacement of all "fork" tokens to become "vfork". This assumes that the original files used the fork call. This also is a bad solution, as it is confusing and is not what you see is what you get when looking at the code. but can be useful to eliminate the need to edit many files manually or add ifdef everywhere where the call is made. third solution ============== have one file (package in Ada terms) that contains all the specific platform functions that are not command to other platforms. so in the above example we can have program.c VMS.c UNIX.c +---------------------------+ +-------------------+ +---------------------+ | | |#include "unistd.h"| |#include "unistd.h" | | #include "program.h" | |int GENERIC_fork() | | int GENERIC_fork() | | status = GENERIC_fork() | | { | |{ | | | | return vfork(); | | return fork(); | +---------------------------+ | } | |} | +-------------------+ +---------------------+ program.h +--------------------------+ | int GENERIC_fork(); | +--------------------------+ Now, this is better. When on UNIX, the makefiles will link the common source code against the UNIX specific files, and when on VMS the makefiles will be directed to link against the VMS specific platform dependent file(s) as in: on VMS: cc -c VMS.c cc program.c VMS.o on UNIX: cc -c UNIX.c cc program.c UNIX.o This solution I like the most of the three. first, the common source code is free of ifdef stuff. and so easier to read and maintain. it also easy to find where all the platform specific stuff is located. the platform specific file(s) should also be named to reflect the platform, but this is minor point. The only problem with the third solution is that one needs to design the program from the start with this in mind. i.e. any operation which could possibly be different on different platforms must be not called directly from the "common" body of the program, but instead wrap it and make the platform specific calls from the platform specific module(s). many time, people do not do this (how many of us who code in c/c++ wraps a fork() call for example? or an open() or etc..etc.., but later on, when one wants to port the program to a different platform, and then find out that that call is not exactly the same as on the first platform, the easy and the quickest way will be to resort to solution 1 above, in particular when the program needs to be done by Monday morning :) I think the hardest thing is to foresee in advanced what the platform specific calls will be. or what the platform specific components will be. for example reading a directory file I do not think is available on windows as is done on UNIX. and so this have to be wrapped (or abstracted). I assume what you meant by point (a) is something along the lines of the third solution above. right? I am not sure about your point (b) above, since it seems to be folded into (a), at least in the way I thought about (a). thanks, Nasser