2. Basics: Daylight Toolkit ObjectsBack to Table of Contents 2.1 Introduction to ObjectsThe Daylight Toolkit uses an object mechanism to simplify the task of programming for chemistry. We begin with a short example that illustrates the object-aspects of the Daylight Toolkit (many other examples come on the tapes with the Toolkits). /*---------------------------------------------------- * thorload.c -- a simple program to load data into * a THOR database. *---------------------------------------------------*/ #include <dt_smiles.h> #include <dt_thor.h> main(int argc, char **argv) { dt_Handle server, db, tdt; char dbname, servername; char *tdtbuf; int tdtbufsize, tdtlen, isnew; /**** Specify the server (machine) and database name ***/ servername = "my_machine_name"; database = "medchem02demo"; /**** Connect to server and open database ****/ server = dt_thor_server(strlen(servername), servername, 4, "thor", 4, "thor", 0, ""); db = dt_open(server, strlen(dbname), dbname, 1, "w", 0, "", &isnew); /**** Load data until input is exhausted ****/ while (1 == du_readtdt(stdin, &tdtlen, &tdtbuf, &tdtbufsize)) { tdt = dt_thor_str2tdt(db, tdtlen, tdtbuf, 1); dt_thor_tdtput(tdt, 0); } dt_dealloc(server); }The important features of this example are:
Many programmers will recognize the similarity of this approach to Object-Oriented Programming (OOP). Although many of the ideas described here are borrowed from OOP, the Daylight Toolkit is not as complete or complicated as a true OOP system. However,the Toolkit uses the following key OOP concepts:
2.2 HandlesAs noted above, handles are the Toolkit's "name" for each object that it creates, and the handle is the only thing your application program knows directly about the object's representation. Because objects are opaque, it is irrelevant to you what a handle actually represents (in fact, different versions of the Toolkit use different methods to assign handles to objects). Although handles are opaque, they have several properties that are important to the application programmer. These properties are the only ones that the Toolkit guarantees: Uniqueness: Each handle is guaranteed to be unique at all times:
Note that uniqueness is not guaranteed over time: the Toolkit may re-use a handle if the original object it represents is discarded. Revocation: Some toolkit functions cause previously-returned handles to become invalid. For example, the handle for an object becomes invalid if the object is removed from the system with dt_dealloc(). A handle that has become invalid in this way is said to have been revoked. Generally speaking, all operations on revoked handles produce undefined results. It is up to the application programmer to guarantee that revoked handles are not used. For functions that cause revocations, the specific description of each function in the Daylight Programmer's Reference Manual will say exactly which handles are revoked. Vigilance: To assist programmers during code development, "vigilant" versions of the Daylight Toolkit are available. These versions may be able to detect the use of an invalid handle. In other words, some toolkit implementations do define a behavior when an operation is applied to a revoked handle. In such vigilant versions, passing a revoked handle to a toolkit function will cause an error return. For extra help in detecting errors, a function named dt_invalid() may be used to test the validity of a handle; it is explained more fully in the chapter entitled POLYMORPHIC FUNCTIONS. A second vigilance function, dt_vh_stop_here(), is provided for use with a debugger. The Toolkit calls this function when an invalid handle is detected.
2.3 Object TypesThe Daylight Toolkit supports a small number of object types. These are divided into several sections, corresponding to the Toolkit's parts (e.g. SMILES, Depict, THOR, etc.). Each of these object types is explained in more detail in the chapter for that section of the Daylight Toolkit; here we give an abbreviated list as an introduction to the object-type concept. General: SMILES: Depict: THOR Each of the above object types is represented by a symbolic constant:
and so forth. The exact symbolic names for each object type can be found in the Daylight Programmer's Reference Manual. There are two "pseudo object" types: TYP_ANY and TYP_INVALID. The pseudo object type TYP_ANY is used when any object is acceptable. Since it is a pseudo object type, there are no actual objects of type TYP_ANY. Similarly, TYP_INVALID may be returned by functions to indicate that the specified object is unknown or incorrect. There are no actual objects of type TYP_INVALID.
2.4 The NULL_OB HandleOne special handle value is used to represent "nothing"; it indicates that no object is present. It is called the null object, and its handle is represented by the symbolic constant NULL_OB. A handle whose value is NULL_OB is a valid handle, but it does not refer to any object and it has no type. NULL_OB plays a special role in the Daylight Toolkit: Functions that return objects will return NULL_OB if an error occurs, and functions that take object parameters will accept NULL_OB as a valid handle (they ignore it and do nothing). This means that error management in applications that use the Toolkit is somewhat simplified -- in many cases the handle returned by one function can be safely passed to the next function whether the first function failed (returned NULL_OB) or succeeded (returned a handle to a real object). It is safe to pass NULL_OB anywhere a handle is expected. See the chapter on error handling for more discussion of this topic. NOTE: In current implementations, NULL_OB is defined to be zero. However, there is no guarantee that this will always be the case. Application programs should explicitly compare for equality or inequality to NULL_OB rather than using constructs like "if (!my_handle) ...". Programs that assume NULL_OB is zero are explicitly non-portable. 2.5 Daylight Version HandlingThe Daylight Toolkit has both Runtime version handling and Compile time version handling. The runtime version handling can be used in the user code to show which version of the runtime libraries are currently being used. The user code can compare the version number to the current Daylight release version and if it is different print an error message describing version inconsistency along with a suggestion to check LD_LIBRARY_PATH which tells the code which runtime libraries to use. The runtime version and creation date can be accessed with the dt_info() function. If the dt_info() function is called with the "toolkit_version" parameter with the runtime libraries made with version 4.81 or later it will return a version number. Any libraries made prior to 4.81 will return NULL. The compile time versions refer to when the entire toolkit was compiled. These versions are described in dt_smiles.h with DX_TOOLKIT_VERSION and DX_TOOLKIT_DATE. These are also the versions numbers and dates that are referenced in the man pages and other Daylight documentation. The user can use DX_TOOLKIT_DATE and DX_TOOLKIT_VERSION to ensure that they are compiling their code with the correct runtime libraries. Example of using runtime and compile time versions.
3. Basics: Polymorphic FunctionsBack to Table of Contents
3.1 PolymorphismThere are many functions, such as counting, copying, deallocating, and naming, that can be applied to several different types of objects. We refer to these functions as polymorphic. The idea of a polymorphic function might seem foreign at first, but it is actually quite familiar to all programmers. Take, for example, the "*" operator in FORTRAN. When applied to two numbers, we expect it to cause the two numbers to be multiplied. However, on closer inspection, the "*" operator turns out to be polymorphic: it can be applied to integers, single-precision floating-point numbers, double- precision floating-point numbers, and complex numbers. The difference between the FORTRAN style of polymorphism and that employed by the Daylight Toolkit is only that the nature of the operation is determined at run time rather than at compile time. That is, the FORTRAN compiler looks at the operands and decides which of several functions to apply, then generates the appropriate code; at run-time the decision as to which function to apply has already been made. In the Daylight Toolkit, a dispatch function examines the object of interest and decides "on the spot" (i.e. at run time) which function to apply. Not all polymorphic functions can be applied to all objects. The following two sections respectively describe "generic" polymorphic functions (those that apply to all objects) and "semi-generic" polymorphic objects (those that could apply to more than one object but not to all objects). NOTE: The specific behavior of polymorhic functions when given different object types is rigorously defined in the reference manual pages. As a simple example of the power of polymorphism, the following function accepts any object and prints out all of its string value(s): dt_Integer dump_strings(dt_Handle ob) { dt_Handle m, d; dt_String line; dt_Integer len; /* Get the stringvalue */ line = dt_stringvalue(&len, ob); /* If the object has one, print it. */ if (line != NULL) fprintf(stderr, "Stringvalue is: %.*s\n", len, line); /* Check to see if the object is a stream or sequence. If so, examine the members also.*/ if ((dt_type(ob) == TYP_STREAM) || (dt_type(ob) == TYP_SEQUENCE)) { dt_reset(ob); while (NULL_OB != (m = dt_next(ob)) && !dt_atend(ob)) dump_string(m); /* Call recursively for each member. */ } return (TRUE); } The important features of this example are:
3.2 Generic FunctionsThe following work on all Daylight Toolkit objects.
3.3 Semi-Generic FunctionsThe following functions are generic in that they apply to more than one object type, but there may be object types to which they do not apply.
Go to previous chapter: Introduction. Go to next chapter: Error Handling |
||||||||