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,690ce1f62160d05a,start X-Google-Attributes: gid103376,public X-Google-Thread: 1094ba,690ce1f62160d05a,start X-Google-Attributes: gid1094ba,public X-Google-Thread: 1014db,690ce1f62160d05a,start X-Google-Attributes: gid1014db,public X-Google-Thread: 109fba,690ce1f62160d05a,start X-Google-Attributes: gid109fba,public From: "E. Robert Tisdale" Subject: How to Design an Application Programmers' Interface (API) Date: 2000/08/10 Message-ID: <39921178.819F6DCB@netwood.net> X-Deja-AN: 656609278 Content-Transfer-Encoding: 7bit Organization: Posted via Supernews, http://www.supernews.com X-Accept-Language: en Content-Type: text/plain; charset=us-ascii MIME-Version: 1.0 Newsgroups: comp.lang.c++,comp.lang.c,comp.lang.fortran,comp.lang.ada X-Complaints-To: newsabuse@supernews.com Date: 2000-08-10T00:00:00+00:00 List-Id: The class library developer writes library functions, perhaps professionally, which other programmers will use to write application programs. The application programmer is the person who actually writes programs, perhaps professionally, and is not just another library developer. The library developers and application programmers negotiate an Application Programmers' Interface (or an Application Program Interface) so that they can work more or less independently of each other. Usually, this negotiation amounts to nothing more than application programmers accepting one of the existing class libraries which may or may not be implementations of an existing standard API. (A standard API permits programmers to write portable applications even if the libraries themselves are not portable.) The assumption is that there are just a few library developers and many application programmers so that the cost of developing and maintaining a library can be amortized over several applications. An API specifies the Abstract Data Type (ATD) which the library supports together with a language binding for each of the supported computer programming languages. The various language bindings may not necessarily resolve to exactly the same objects in a library archive because that is an implementation issue which is best left up to the library developer. Usually, the API does not specify interoperability between programs and subroutines written in different languages. The secret to designing a good API is abstraction. The API should specify no more detail about the actual data representation or algorithm implementation than is necessary to provide a practical API. There is virtually no penalty for abstraction built into modern, object oriented computer programming languages like C++. Unfortunately, some C++ compilers still impose a penalty for abstraction but there is usually a suitable workaround that is transparent to the application programmer so the problem is manageable. Older computer programming languages, such as Fortran 77, won't even permit library developers to substitute synonyms for built-in types let alone permit library developers to define new types so abstraction can be very difficult to preserve. A good API, like anything else, should be designed from the top down. Now, of course, everyone should be aware that good engineering requires several cycles of top down and bottom up design. All top down design really means is that the first stroke is top down. The design should begin with the abstractions which are to be resolved in the implementation. The first implementation should expose most of the practical difficulties of the design which can be used to work back up to eventually resolve problems with the original abstraction. Some care should be taken to consider all possible alternate data representations and algorithm implementations so that the API does preclude any reasonable representation or implementation. It is important to be very precise about the definition of the ADT. Once the ADT is correctly and precisely identified, designing the rest of the API is relatively straight forward. A vague description of an ADT which is too general to implement efficiently is probably better represented by two or more similar but distinct ADTs each of which can be implemented efficiently. The API should specify all of the functionality required to support the ADT. This is almost as easy to do as it is to say for the classical ADTs which are implemented in the standard C++ library -- list, queue, etc. But it can be a nontrivial task for the ADTs supported by a numerical class library for example. There appear to be a very large number of functions that operate on vector, matrix and tensor objects. The API should specify all of this functionality even if it doesn't require that library developers support all of it. The reason is that application programmers are going to implement unsupported functionality whether it is specified or not. But, if it is specified, application programmers can, at least, implement the functionality as specified by the API so that other application programmers will be able to more easily read, understand and maintain the application source code. In other words, the API need not mandate everything that it specifies. The API should mandate only that functionality which is required to implement the all of the specified functionality so that application programmers can write portable versions of the specified functions to replace any missing functionality. This liberates library developers to implement as much or as little of the remaining functionality as they see fit. Application programmers who write their own libraries are at a natural disadvantage when it comes to designing an API because they virtually always have a particular implementation in mind and begin to code before they define the ADT precisely. They only discover their mistake when they realize that they need to change the data representation or algorithm implementation and find themselves searching through their application source code to find all of the references that need to be changed. The API designer preserves abstraction by hiding the actual details of the data representation and algorithm implementation from application programs so that application programmers don't accidently reference them directly. These details are not necessarily secret. They may be published in documents or even source code but they should be distinguished (and separated if possible) from the specification of the API. C++ API implementations hide the details of the actual data representation behind a simple private: or protected: label and provide member functions to access them. C API implementations use opaque data types to hide private data members. C++ API implementations use inline functions to avoid the abstraction penalty. C API implementations may be obliged to use preprocessor macros to avoid the abstraction penalty. Fortran 77 requires custom preprocessing to avoid the abstraction penalty. API designers seldom take abstraction seriously enough. Generally, it's a good idea to take advantage of existing especially standard data types but it is seldom a good idea to expose such implementation details to the application program. Usually, simple synonyms are sufficient to provide the required abstraction. For example, typedef float Single; typedef double Double; typedef int Integer; typedef size_t Size; may permit programmers to write more portable application source code if they use the synonyms instead of the built-in types.