Synonyms for robust at Thesaurus.com with free online thesaurus, antonyms, and definitions. Dictionary and Word of the Day. Secrets to a Robust Telework Program. For some, the drivers for a robust telework program are a lack of office space or a need to reduce operating costs.
Robust Programming. Department of Computer Science. University of California at Davis. Davis, CA 9. 56. 16- 8. Principles of Robust Programming. A robust program differs from a non- robust, or. Paranoia. Don't trust anything you don't generate! Define robust: strong and healthy — robust in a sentence. DRAFT PAPER - do not copy Robust Design Techniques for C Programs David Turner ([email protected]) Abstract. Robust Design DefinitionWhenever someone uses your program or library routine, assume they will try to break it. When you call another function, check that it succeeds. Most importantly, assume that your own code will have problems, and program defensively, so those problems can be detected as quickly as possible. Assume that the caller or user is an idiot, and cannot read any manual pages or documentation. Program so that the code you write will handle incorrect, bogus, and malformed inputs and parameters in a reasonable fashion, . For example, if you print an error message, the message should be self- explanatory and not require the user to look up error codes in a manual. What Is Robust DesignIf you return an error code to the caller (for example, from a library routine) make the error codes unambiguous and detailed. Also, as soon as you detect the problem, take corrective action (or stop). This keeps the error from propagating. This programming style is also a form of defensive programming. For example, in the standard I/O library, the contents of the FILE structure for allocated files is expected to remain fixed across calls to the standard I/O library. They might accidentally (or deliberately) modify the data in that data structure, causing your library functions to fail - badly. Never return pointers or indices into arrays; always hide the true addresses (or indices) by using something called a . For example, the queue manager uses arrays to represent queues. This gives them a maximum size. You might decide that linked lists would be more suitable and want to rewrite the routines. If you have properly designed the interface and hidden as much information and data as possible, the calling program need not be changed; however, if you neglected this style of information hiding and information abstraction, programs that correctly function with the current representation might break if you changed that representation (because the caller assumed that the queue elements are in sequential integer locations, for example). Think about how those cases should be handled, and implement that type of handling. In the worst case, check for what you think is impossible, and print an error message if it occurs. After all, if you modify the code repeatedly, it is very likely that one modification will affect other parts of the code and cause inconsistent effects, which may lead to . Relate the informal descriptions of the principles of robust programming to the more formal principles of good programming style, such as cohesion and coupling, information abstraction, information hiding, and good documentation. The point of this exercise is to show you that robust programs arise from writing code in a good style; what you learn in school. Programming is not mathematics; errors occur. Good programming assumes this, and takes steps to detect and report those errors, internal as well as external. Beware of everything - even your own code! It's typical of what many C programmers would write. It's also very fragile code. We'll analyze it in detail to support that statement. Three entry points are provided. All files calling this routine must include the header file. Because programs calling the queue functions must pass a pointer to the queue, the structure must be visible to the calling procedures (which need to know the structure to define the queue pointer type QUEUE *). Hence this file must be included in programs that call the queue library functions. The caller will use a pointer to a QUEUE structure; and as the layout of that structure, and its location, is known, the caller can bypass the queue library to obtain data from the queues directly - or alter information in the queues, or information that the library uses to manage the queues. For example, if one wanted to change the number of elements in the queue without calling a queue management function, one can say: qptr- > count = newvalue. The operations could be written as separate functions. That this routine has such poor cohesion does not speak well for its robustness. The. parameter is an integer that indicates whether a queue is to be created (if. The. parameter gives the maximum number of integers to be allowed in the queue. Suppose a caller interchanged the two: qmanage(& qptr, 8. This ease of confusion in the parameters is the first problem, and one that cannot be checked for easily. When does it mean to create a queue and when does it mean to delete a queue? For this function, the intention is that 1 means create and 0 means delete, but the coding style has changed this to allow any non- zero value to mean create. But there is little connection between 1 and create, and 0 and delete. So psychologically, the programmer may not remember which number to use and this can cause a queue to be destroyed when it should have been created. Using. s would help here if the library is compiled with the program, but if the library is simply loaded. Passing a pointer provides the call by reference mechanism. However, a call by reference usually uses a singly indirect pointer; if a doubly indirect pointer is used, programmers will make mistakes (specifically, pass a singly indirect pointer). In general, it is better to avoid call by reference; when it is necessary, do not use multiple levels of indirection unless absolutely necessary. First look at queue creation. Suppose. or an invalid pointer. Then the line containing. Also, suppose. is non- positive. What happens when the queue is allocated (the second. If. is negative, the result is implementation dependent and may cause a segmentation violation. In either case, the result is unpredictable. The parameter. is irrelevant, but suppose. NULL. Then the result of. Worse, imagine the parameter is not. This is almost impossible to catch before the call, and causes segmentation violations (if lucky) or very odd behavior afterwards (if not). Suppose one deletes a queue before creating it: qmanage(& qptr, 0, 1). Again, the problem is that qmanage does not check that qptr refers to a valid queue. However, here's a more subtle variation of this problem: qmanage(& qptr, 1, 1. Attempting to. space that has already been deallocated causes an undefined action, usually a segmentation violation. What happens if either. NULL? The subsequent references to. Consider the expressionsize * sizeof(int). Suppose size is 2. On a 3. 2 bit machine, this overflows, and (most likely) produces a value of 0. Such a flaw will most likely not cause any problems during the call, but will cause the program to produce a segmentation fault in a seemingly unrelated place later on. Overflow (and underflow, in floating point calculations) are very pernicious and nasty problems; watch out for them. Does this always work? What problems does it introduce? It adds the index of the head element to the current count (modulo the queue size), and places the new element at that location; it then increments the count: /*. PARAMETERS: QUEUE *qptr pointer for queue involved. First, the qptr parameter is not checked to ensure it refers to a valid queue; it may refer to a queue that has been deleted, or to random memory, or may be. NULL. Any of these will cause problems; if the caller is lucky, the problem will arise in this routine; if the caller is unlucky, the symptom will not appear until later on in the program, with no indication of what caused it. Then the routine will not work correctly. If this function is called, it will overwrite the head of the queue. There is no check for an overflowing queue, doubtless because the author assumed it would never happen. The integrity of the queue structure depends on the consistency of the. If. increases between calls, the routine will think that the queue has been allocated more space than it actually has, leading to a segmentation fault. If. decreases between calls, some elements might be lost. Exercise 3. Describe the problems that can arise if the values of. Then count is decremented, and. PARAMETERS: QUEUE *qptr pointer for queue involved. Moreover. may also be an invalid integer pointer. Suppose there are no elements in the queue. The value returned through. What problems might an invalid pointer for. Specifically, suppose in the call. How would you solve these problems in a portable manner? Among its flaws are. The callers have access to the internal elements of the queue structure. This version, however, is very robust; it performs sanity checking, and attempts to anticipate problems and handle them gracefully. If the library cannot recover, it returns an error code to the caller indicating the problem. This code is more complex to write, but- -as the callers can rely on it- -makes debugging the calling applications much simpler. We deal with the interface first. The object that the caller uses to represent a queue will be called a. Hence we need some other mechanism, and indexing into an array is the obvious solution. However, if we use simple indices, then the user can refer to queue 0, and have a high degree of certainty of getting a valid queue. So instead we use a function of the index such that 0 is not in the range of the function. Thus, we will represent the queues as entries in an array of queues, and the token will be an invertible mathematical function of their index. Suppose a programmer uses the library to create queue A. Queue A is subsequently deleted and queue B created; queue B happens to occupy the same slot in the array of queues as queue A did. A subsequent reference to queue A by token will get queue B. To avoid this problem, we associate with each queue a unique integer (called a. In the above example, queue might have nonce 1. B might have nonce 3. The token for queue A is. B is. f(7, 3. 08. As these values differ, the reference to queue A will be detected and rejected. We emphasize, however, that.
0 Comments
Leave a Reply. |