1 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
Contents
1 Module
1.1 Interface
1.2 Implementation
2 Test Programs
2.1 Running the Test Program
3 Object Module Libraries
3.1 Library Header File
3.2 A Sample Module
3.3 Building the Library
4 Notes
Module
Interface
General.h
20-11-2014 13:04
2 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
#ifndef GENERAL_H
#define GENERAL_H
...
...
Following macro definitions stand for constructor types that one should consider in the process of
implementing an abstract data type.
#define COPY 0
#define DEFAULT 1
...
Creating a copy of a Vector object from an already existing one involves copying its components too. With
a little insight we can see that there is no universal function to serve this purpose. As a matter of fact, same
data type may require different such functions in different contexts. Same argument applies to the case of
destroying a Vector object.
So, are we (the programmers) supposed to provide each and every possible copy and destruction functions?
This beats the purpose of providing a generic container. We have to get the user to cooperate with us. And
the way to do so is through the following pointer-to-function type definitions.
Vector.h
1.
#ifndef VECTOR_H
2.
#define VECTOR_H
3.
4.
#include "General.h"
5.
6.
struct _VECTOR;
7.
typedef struct _VECTOR* Vector;
We want to implement a generic vector. That is, a vector that can have anything as its component: a vector
20-11-2014 13:04
3 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
of ints, a vector of character strings, a vector of student records, and so on. Given this requirement and the
fact that the structure of the component can be known only to the user [not the implementer], we need to
ask the user for some assistance about copying and destroying components of the vector. Basically, the user
must implement these two functions and somehow pass it to the implementer. In other words, the user and
the implementer must cooperate; the user must fill in the missing parts in the framework that is provided by
the implementer. A user maintaining a Vector of student records has to provide copy and destruction
functions for student record type; a user dealing with employee records has to provide the same functions for
employee record type; ...
This style of programming differs from the conventional technique, where the user of the library develops
algorithms that invoke library functions, but the overall structure of the application and flow of control
differs for each new application. Although almost any C program makes use of the I/O library, the overall
structure of a particular program is independent of others; routines provided in the I/O library are simply
'low-level' operations common to all programs and do not have to be tailored to the needs of a particular
application. This is the technique used with procedural programming languages such as Pascal and C.[2]
In software framework usage, however, it is the overall structure, the high-level view of the algorithm that
remains constant and is reused from application to application. The programmer (the user of the framework)
specializes the framework, but majority of code remains the same across all applications. Because of this
inversion in the relationship between library code and the user-supplied code, a software framework is
sometimes referred to as an "upside-down" library.
The best well-known software frameworks are those associated with graphical user interfaces. The major
tasks involved in executing a graphical program- waiting for the user to move or click the mouse, waiting for
keystrokes, moving or resizing windows, and so on- do not change much from one application to another.
Thus these actions can be usefully combined in a single framework not only to save programming effort in
developing new applications, but also to ensure consistency among applications. What distinguishes one
program from another are features such as the contents of windows and the action to be performed in
response to a mouse click or keystroke. It is these actions that must be programmed anew in each specific
application of the framework.
This is the style of choice in object-oriented programming languages. To create a new application, it is
necessary only to subclass from the original class [using inheritance] and then implement the behavior
associated with these deferred functions. All other functionality, including the overall structure of the
application, is obtained for free through the use of inheritance. Popular examples are the C++-based MFC
20-11-2014 13:04
4 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
9.
typedef struct _Object_funcs {
10.
COPY_FUNC copy_component;
11.
DESTRUCTION_FUNC destroy_component;
12.
} Vector_Component_Funcs;
13.
14.
#define NON_EXISTING_VECTOR -1
15.
16.
#define INIT_CAP 2
17.
#define BOTH 3
The following function signature is an example to the declaration of variadic functions in C. Ellipsis used in
the specification signifies an unknown number of parameters coming after the named arguments.[3] Number
of arguments passed to a variadic function is deduced from the contents of one or more of the named
arguments. Take the printf function for instance, whose prototype is provided below.
int printf(const char *format_string, ...);
Placeholders, certain character sequences starting with %, in format_string help in figuring out the number
and type of arguments.
19.
extern Vector Vector_Create(int constructor_type, ...);
20.
extern int Vector_Destroy(Vector* this);
21.
extern void Vector_Assign(Vector* lhs, const Vector rhs);
22.
extern Object Vector_ElementAt(const Vector this, unsigned int pos);
20-11-2014 13:04
5 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
23.
extern Object Vector_InsertAt(const Vector this, Object item, unsigned int pos);
24.
extern Object Vector_RemoveAt(const Vector this, unsigned int pos);
25.
extern Object Vector_UpdateAt(const Vector this, Object new_item, unsigned int pos);
26.
extern unsigned int Vector_Size(const Vector this);
27.
28.
#endif
Implementation
Vector.c
1.
#include <stdarg.h>
2.
#include <stddef.h>
3.
#include <stdio.h>
4.
#include <stdlib.h>
5.
#include <string.h>
6.
7.
#include "General.h"
8.
#include "utility/Vector.h"
For controlling the lifetime of a particular object shared among different handles (actually pointers in our
case), we use an extra field to hold the number of handles that provide access to the same underlying object.
Such a field is called a reference count.
The last field of the structure definition can be seen, for pedagogical purposes, as Object
container[];. That is, a Vector is basically an array of Objects. Using * instead of [] gives us the
flexibility of changing the size of our Vector dynamically. Had we used [] we would have had to specify
the size at compile time, which would have been too restrictive for our purposes.
10.
struct _VECTOR {
20-11-2014 13:04
6 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
11.
COPY_FUNC copy_component;
12.
DESTRUCTION_FUNC destroy_component;
13.
unsigned int ref_count;
14.
unsigned int cap;
15.
unsigned int cap_inc;
16.
unsigned int cur_size;
17.
Object *container;
18.
};
19.
20.
static BOOL adjust_capacity(const Vector);
21.
static void shift_down(const Vector, unsigned int);
22.
static void shift_up(const Vector, unsigned int);
23.
24.
Vector Vector_Create(int type, ...) {
25.
int i;
The following line declares a variable that will be used in traversing the list of unnamed arguments.
26.
va_list ap;
27.
Vector existing_vec;
28.
Vector_Component_Funcs comp_funcs;
29.
30.
Vector ret_vec = (Vector) malloc(sizeof(struct _VECTOR));
31.
if (!ret_vec) {
32.
fprintf(stderr, "Out of memory...\n");
33.
return NULL;
34.
20-11-2014 13:04
7 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
} /* end of (!ret_vec) */
35.
36.
ret_vec->cur_size = 0;
37.
ret_vec->ref_count = 1;
38.
ret_vec->container = NULL;
The following macro is used to initialize the list of unnamed arguments so that an internal pointer in ap
points right after the last named argument.
40.
va_start(ap, type);
41.
switch (type) {
42.
case COPY:
Next line returns an argument of type Vector and advances the internal pointer so that it points to the
location right after this Vector argument. The run-time stack after execution of this statement is as shown
below:[4]
20-11-2014 13:04
8 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
43.
existing_vec = va_arg(ap, Vector);
This is where we retrieve the function pointers passed by the user. Each time we need to copy or destroy a
component we will call back to the functions found at the given addresses.
44.
ret_vec->copy_component = existing_vec->copy_component;
45.
ret_vec->destroy_component = existing_vec->destroy_component;
46.
ret_vec->cap = ret_vec->cur_size = existing_vec->cur_size;
47.
ret_vec->cap_inc = existing_vec->cap_inc;
In addition to malloc, we can use calloc to allocate a region of memory from the heap. This function
allocates memory large enough to hold n (passed as the first argument) elements, each of size m (passed as
the second element); it returns a pointer to the first element of the array. This can certainly be done by
malloc. As an extra, the returned region is bitwise zeroed, which is not done by malloc.
Memory allocated calloc can be returned using free or cfree. But keep in mind that despite its
omnipresence cfree is not a standard C function.
20-11-2014 13:04
9 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
Question:
Can we use calloc to allocate memory that holds an int with an initial value of 0? What about a
float with an initial value of 0.0? Can you guarantee that answer will always be the same?
float *f = (float *) calloc(1, sizeof(float));
/* What is the value contained in *f? Is it 0.0? */
int *i = (int *) calloc(1, sizeof(int));
/* What is the value contained in *i? Is it 0? */
Answer
Given the ubiquity of number representation standards the answer to the first part of question is very
likely to be affirmative. However, we still cannot give any guarantees.
48.
ret_vec->container = calloc(existing_vec->cur_size, sizeof(Object));
49.
for(i = 0; i < existing_vec->cur_size; i++)
50.
ret_vec->container[i] = existing_vec->copy_component(existing_vec->container[i]);
51.
break;
52.
case DEFAULT:
53.
comp_funcs = va_arg(ap, Vector_Component_Funcs);
54.
ret_vec->copy_component = comp_funcs.copy_component;
55.
ret_vec->destroy_component = comp_funcs.destroy_component;
56.
ret_vec->cap = ret_vec->cap_inc = 0;
57.
break;
58.
case INIT_CAP:
59.
comp_funcs = va_arg(ap, Vector_Component_Funcs);
60.
ret_vec->copy_component = comp_funcs.copy_component;
61.
ret_vec->destroy_component = comp_funcs.destroy_component;
20-11-2014 13:04
10 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
62.
ret_vec->cap = va_arg(ap, unsigned int);
63.
ret_vec->cap_inc = 0;
64.
if (ret_vec->cap <= 0) break;
65.
ret_vec->container = calloc(ret_vec->cap, sizeof(Object));
66.
break;
67.
case BOTH:
68.
comp_funcs = va_arg(ap, Vector_Component_Funcs);
69.
ret_vec->copy_component = comp_funcs.copy_component;
70.
ret_vec->destroy_component = comp_funcs.destroy_component;
71.
ret_vec->cap = va_arg(ap, unsigned int);
20-11-2014 13:04
11 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
72.
ret_vec->cap_inc = va_arg(ap, unsigned int);
73.
if (ret_vec->cap <= 0) break;
74.
ret_vec->container = calloc(ret_vec->cap, sizeof(Object));
75.
break;
76.
} /* end of switch(type) */
va_end performs necessary cleanup operations on ap. In other words, it serves the purpose of a destructor.
It should be called by the programmer after all arguments have been read with va_arg.
77.
va_end(ap);
78.
79.
return(ret_vec);
20-11-2014 13:04
12 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
Question:
Which one of the following memory layouts is more reasonable for implementing variadic functions?
Answer
We know that named arguments and local variables are at fixed LB-relative addresses. Depending on
whether the run-time stack grows downward or upward, if arguments are at (LB offset) then local
variables are at (LB + offset); if arguments are at (LB + offset) then local variables are at (LB
offset). Such an arrangement enables the compiler to fix LB-relative addresses of these items at
compile-time. Now that memory reserved for unnamed arguments can vary depending on the number
and size of such items, the above scheme becomes impossible to guarantee with the choice on the right
side. So, the correct answer is right-to-left.
Insert figure
The names are due to the order of push operations. In right-to-left, first argument to be pushed on the
run-time stack is the rightmost argument in the argument list, while in left-to-right it is the leftmost that
is pushed first.[5]
For this reason, a programming language supporting variadic functions, such as C, will naturally use
right-to-left, while a language like Pascal, which doesnt support variadic functions, can choose either
arrangement. In a call to non-variadic function a C compiler may be (by means of a environment
specific directive) directed to pass the arguments in the opposite direction.[6]
Question:
In C, which one do you think is responsible for removing the arguments from the run-time stack, caller or
the callee?
Answer
Compiled code corresponding to the callee, just like any other code, is immutable. This means the code
template used for removing the arguments is fixed. However, the number of bytes pushed on the
runtime stack varies with the number of arguments and their types and this bit of information will
probably change for each invocation. This means the compiler cannot foresee the number of bytes
passed to the callee and synthesize a template for argument removal. For this reason, this template is
synthesized differently for each invocation and appended right after the point of call. So, it is the
callers responsibility to remove the arguments from the runtime stack.[7]
80.
} /* end of Vector Vector_Create(Constructor_type, struct basic_funcs, ...) */
20-11-2014 13:04
13 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
The user accesses a Vector object through a handle (this variable in the figure). The representation is
hidden and it is modified only through the functions in the public interface of Vector.
Note that the handle is a pointer to the properties of the Vector, not to its components. This is the typical
approach taken to implement collection types. You have a handle on some metadata, which (in addition to
defining properties of the collection) has a handle on the container that is used to hold the components.
82.
int Vector_Destroy(Vector* this) {
83.
int i;
84.
Vector this_copy = *this;
85.
86.
if (this_copy == NULL) return(NON_EXISTING_VECTOR);
The Vector_Destroy function having been called doesnt mean the underlying object will be destroyed.
In the case of an object that is shared among different handles, the reference count is decremented and the
underlying object is left untouched. If the reference count is one we are about to severe the tie between the
object and the last handle on it. In such a situation, we must also destroy the underlying object!
87.
if(this_copy->ref_count > 1) {
88.
*this = NULL;
89.
return(--this_copy->ref_count);
90.
} /* end of if(this_copy->ref_count > 1) */
Note that destroy_component is a pointer to function. That is, the function is called through a pointer;
we can change the function being invoked by assigning different pointer values. This is actually what gives
us the ability to deal with different types of data.[8]
92.
for (i = this_copy->cur_size - 1; i >= 0; i--)
93.
this_copy->destroy_component(this_copy->container[i]));
94.
20-11-2014 13:04
14 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
95.
free(this_copy->container); free(this_copy);
96.
*this = NULL;
97.
98.
return 0;
99.
} /* end of int Vector_Destroy(Vector*) */
100.
101.
Object Vector_ElementAt(const Vector this, unsigned int pos) {
102.
Object ret_object;
103.
104.
if (pos >= this->cur_size) return NULL;
See that we return a copy of the required component, not a pointer to it. The rationale behind this is to avoid
giving any access to the underlying data structure.
105.
ret_object = this->copy_component(this->container[pos]);
106.
107.
return(ret_object);
108.
} /* end of Object Vector_ElementAt(const Vector, unsigned int) */
Next function assigns a Vector object to another. Since we dont have operator overloading in C, we have
to come up with a function name and use this name to perform assignment. Of course, we could still use '='
to do this. But, doing so would lead to an inconsistent state, where reference count and the number of
handles sharing the underlying object do not agree.
20-11-2014 13:04
15 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
The previous code fragment will produce the picture given below.
Insert figure
110.
void Vector_Assign(Vector* lhs, const Vector rhs{
111.
Vector_Destroy(lhs);
112.
*lhs = rhs;
113.
rhs->ref_count++;
114.
} /* end of void Vector_Assign(Vector*, const Vector) */
115.
116.
Object Vector_InsertAt(const Vector this, Object new_item, unsigned int pos) {
117.
if (this->cur_size < pos) pos = this->cur_size;
Before we make the insertion, we have to make sure there is enough room in our structure. If not, we adjust
the container capacity according to some predefined formula. In case we may be out of heap memory, we do
not effect any changes in the structure and just return a NULL value from the function. Otherwise, we extend
the memory region needed for the container and go ahead with the insertion: if the new component is
inserted at the end just copy it at the end of the vector, which is indicated by the cur_size field; if the new
component is inserted somewhere other than the end, shift the components that are to come after location of
the new item down by one and make the insertion.
118.
if (this->cap == this->cur_size)
119.
if (!adjust_capacity(this)) return NULL;
120.
if (pos != this->cur_size) shift_down(this, pos);
121.
this->container[pos] = this->copy_component(new_item);
122.
this->cur_size++;
123.
124.
return new_item;
125.
} /* end of Object Vector_InsertAt(const Vector, Object, unsigned int) */
126.
127.
20-11-2014 13:04
16 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
130.
if (pos >= this->cur_size) return NULL;
131.
ret_object = *(this->container + pos);
132.
if (pos != this->cur_size - 1) shift_up(this, pos);
133.
this->cur_size--;
134.
135.
return(ret_object);
136.
} /* end of Object Vector_RemoveAt(const Vector, unsigned int) */
137.
138.
Object Vector_UpdateAt(const Vector this, Object new_item, unsigned int pos) {
139.
Object old_value;
140.
141.
if (pos >= this->cur_size) return NULL;
142.
143.
old_value = *(this->container + pos);
144.
this->container[pos] = this->copy_component(new_value);
145.
146.
return old_value;
147.
} /* end of Object Vector_UpdateAt(const Vector, Object, unsigned int)*/
148.
149.
unsigned int Vector_Size(const Vector this) { return(this->cur_size); }
adjust_capacity function extends the capacity of a Vector according to some given formula. In our
implementation, we adopt the formula used in most C++ compilers: if the Vector has a nonzero capacity
increment value, capacity is incremented by this amount; if the user has not provided any value for this field
or it is zero, the new capacity is calculated by doubling the previous one. (If the current capacity value is
zero then new capacity is taken to be one.) Failure to allocate enough room for the Vector object results in
returning a FALSE value.
20-11-2014 13:04
17 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
151.
static BOOL adjust_capacity(const Vector this) {
152.
int i;
153.
const Vector old_this = (Vector)
malloc(sizeof(struct _VECTOR));
154.
if(!old_this) {
155.
fprintf(stderr, "Out of memory...\n");
156.
return FALSE;
157.
} /* end of if(!old_this) */
158.
memcpy(old_this, this, sizeof(struct _VECTOR));
159.
160.
if (this->cap_inc != 0) this->cap += this->cap_inc;
161.
else if (this->cap == 0) this->cap = 1;
162.
else this->cap *= 2;
163.
this->container = calloc(this->cap, sizeof(Object));
164.
165.
if (!this->container) {
166.
this->cap = old_this->cap;
167.
this->container = old_this->container;
168.
free(old_this);
169.
fprintf(stderr, "Out of memory...\n");
170.
return FALSE;
171.
} /* end of if(!this->container) */
172.
173.
if (old_this->cap == 0) {
174.
free(old_this);
175.
return TRUE;
176.
} /* end of if(old_this->cap == 0) */
20-11-2014 13:04
18 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
177.
memcpy(this->container, old_this->container,
178.
sizeof(Object) * old_this->cur_size);
179.
180.
free(old_this->container);
181.
free(old_this);
182.
return TRUE;
183.
} /* end of BOOL adjust_capacity(const Vector) */
Shifting down is required whenever a new item is inserted into any location other than the end of the
Vector.
185.
static void shift_down(const Vector this, unsigned int pos) {
186.
unsigned int i;
187.
for(i = this->cur_size; i > pos; i--)
188.
memcpy(this->container + i, this->container + (i - 1),sizeof(Object));
189.
} /* end of void shift_down(const Vector, unsigned int) */
Similarly, shifting up is required whenever an item is removed from any location other than the end of the
Vector.
191.
static void shift_up(const Vector this, unsigned int pos) {
192.
unsigned int i;
193.
for(i = pos; i < this->cur_size - 1; i++)
194.
memcpy(this->container + i, this->container + (i + 1), sizeof(Object));
195.
} /* end of void shift_up(const Vector, unsigned int) */
20-11-2014 13:04
19 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
Test Programs
Vector_Test.c
1.
#include <stdio.h>
2.
3.
#include "General.h"
4.
#include "Wrappers.h"
5.
#include "utility/Vector.h"
6.
7.
enum {CREATE_COPY = 1, CREATE_DEFAULT, DESTROY, INSERT = 6, PEEK, REMOVE, UPDATE, DISPLAY, EXIT
8.
9.
Vector v[2];
10.
11.
void display_vec(unsigned int which) {
12.
unsigned int i;
13.
14.
if (!v[which]) {
15.
printf("-----\n");
16.
return;
17.
} /* end of if(!v[which]) */
18.
printf("+++++\n");
19.
printf("Contents of vector #%d\n", which);
20.
for (i = 0; i < Vector_Size(v[which]); i++)
21.
printf("#%d: %d\t", i, Integer_Value(Vector_ElementAt(v[which], i)));
22.
printf("\n");
23.
24.
return;
25.
20-11-2014 13:04
20 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
27.
int main(void) {
28.
int val;
29.
Integer pval;
30.
unsigned int action, pos, which_vec, from_which_vec;
31.
Vector_Component_Funcs int_funcs = {&Integer_CopyInteger, &Integer_DestroyInteger};
32.
33.
do {
34.
scanf("%d", &action);
35.
switch (action) {
36.
case CREATE_COPY:
37.
scanf("%d %d", &which_vec, &from_which_vec);
38.
printf("Trying to create a copy of vector#%d into vector#%d...", from_which_vec, which_vec
39.
v[which_vec] = Vector_Create(COPY, v[from_which_vec]);
40.
if (v[which_vec]) printf("+++++\n");
41.
else printf("-----\n");
42.
break;
43.
case CREATE_DEFAULT:
44.
scanf("%d", &which_vec);
45.
printf("Trying to create vector #%d using default constructor...", which_vec);
46.
v[which_vec] = Vector_Create(DEFAULT, int_funcs );
47.
if (v[which_vec]) printf("+++++\n");
48.
else printf("-----\n");
49.
break;
50.
case DESTROY:
51.
scanf("%d", &which_vec);
52.
printf("Trying to destroy vector#%d...", which_vec);
20-11-2014 13:04
21 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
53.
if (!Vector_Destroy(&v[which_vec])) printf("+++++\n");
54.
else printf("-----\n");
55.
break;
56.
case INSERT:
57.
scanf("%d %d %d", &which_vec, &pos, &val);
58.
printf("Trying to insert %d at position %d of vector#%d...", val, pos, which_vec);
Note we insert Integer objectswhich are basically objects of an opaque type that wrap around an intnot
ints. This is a consequence of the genericness provided by means of void*, which requires insertion of
pointers.
59.
pval = Integer_Create(val);
60.
if (Vector_InsertAt(v[which_vec], pval, pos)) printf("+++++\n");
61.
else printf("-----\n");
62.
break;
20-11-2014 13:04
22 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
63.
case REMOVE:
64.
scanf("%d %d", &which_vec, &pos);
65.
printf("Trying to remove the item at position %d from vector#%d...", pos, which_vec);
Now that the value returned (ret_obj) will be assigned to pval, we will have a handle on the removed object
through pval.
20-11-2014 13:04
23 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
66.
pval = Vector_RemoveAt(v[which_vec], pos);
67.
if (pval) printf("+++++%d\n", Integer_Value(pval));
68.
else printf("-----\n");
69.
break;
70.
case PEEK:
71.
scanf("%d %d", &which_vec, &pos);
72.
printf("Trying to peek in vector#%d at position %d...", which_vec, pos);
20-11-2014 13:04
24 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
Realize the object is cloned and then returned. This new object, after assignment to pval, can be manipulated
independently of the original copy.
73.
pval = Vector_ElementAt(v[which_vec], pos);
74.
if (pval) printf("+++++%d\n", Integer_Value(pval));
75.
else printf("-----\n");
76.
break;
77.
case UPDATE:
78.
scanf("%d %d %d", &which_vec, &pos, &val);
79.
printf("Trying to update position %d of vector#%d with %d...", pos, which_vec, val);
80.
pval = Integer_Create(val);
81.
if (Vector_UpdateAt(v[which_vec], pval, pos))
82.
printf("+++++\n");
83.
else printf("-----\n");
84.
break;
85.
case DISPLAY:
86.
scanf("%d", &which_vec);
20-11-2014 13:04
25 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
87.
printf("Trying to display vector#%d...", which_vec);
88.
display_vec(which_vec);
89.
break;
90.
case EXIT:
91.
exit(0);
92.
} /* end of switch(action) */
93.
} while (1);
94.
} /* end of int main(void) */
...
1 Screen
...
2 Screen
...
20-11-2014 13:04
26 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
...
1 Vector_Test.output ...
2 Screen
...
For both before and after remapping, we have the following macro definitions in place:
#define stdin 0
#define stdout 1
#define stderr 2
This is all done by the command-line interpreter. The user doesnt need to make any modifications in the
source code; (s)he can still program as if input were entered at the keyboard and output were written to the
screen. This is possible because we read from or write to a physical file through a handle (logical file). If we
change the physical file the handle is mapped to, although we write to the same logical file (handle) the final
destination affected changes.
Vector_Test.input
2 0
6 0 10 5
6 0 10 15
6 0 8 20
6 0 8 25
6 0 2 30
6 0 0 35
10 0
8 0 2
8 0 3
8 0 100
10 0
1 1 0
8 1 1
7 1 0
9 1 2 26
10 1
7 0 2
7 0 5
10 0
3 0
10 0
0
Vector_Test.output
20-11-2014 13:04
27 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
20-11-2014 13:04
28 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
Definition: A library is an archive of object modules, whichin addition to the modulesgenerally has
some sort of metadata to quicken locating the individual files.
1.
#ifndef WRAPPERS_H
2.
#define WRAPPERS_H
3.
4.
#include "General.h"
Since our generic vector expects an opaque pointer as its component, using it with primitive data may turn
out to be painful: You must first wrap the data and use the pointer to this wrapped data in your transactions.
Later on when you want to print the data to some medium, you must do some unboxingthat is, retrieve the
contents of the region pointed by the opaque pointerand then print the data. Similar things can be said
about comparing, copying, and destroying the data.
This pattern is so common that a special library (libWrappers.a) is offered for the users convenience. In case
(s)he may want to use the Vector module and the like with primitive data types, (s)he doesnt have to
reinvent the wheel; all (s)he has to do is to use the functions found in libWrappers.a.
The whole thing can be likened to the wrapper classes in Java and the concept of automatic boxing-unboxing
in C#. As a matter of fact, this way of using a container is so frequent that Java, as of 1.5, supports automatic
boxing-unboxing.
6.
struct _CHAR;
7.
typedef struct _CHAR* Char;
20-11-2014 13:04
29 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
8.
extern Char Char_Create(unsigned char val);
9.
extern void Char_Destroy(Char* this);
10.
11.
extern int Char_CompareChars(Object, Object);
12.
extern Object Char_CopyChar(Object);
13.
extern void Char_DestroyChar(Object);
14.
15.
extern unsigned char Char_Value(Char wrappedChar);
16.
17.
struct _SCHAR;
18.
typedef struct _SCHAR* SChar;
19.
extern SChar SChar_Create(signed char val);
20.
extern void SChar_Destroy(SChar* this);
21.
22.
extern int SChar_CompareSChars(Object, Object);
23.
extern Object SChar_CopySChar(Object);
24.
extern void SChar_DestroySChar(Object);
25.
26.
extern signed char SChar_Value(SChar wrappedSChar);
27.
28.
struct _INTEGER;
29.
typedef struct _INTEGER* Integer;
30.
extern Integer Integer_Create(signed int val);
31.
extern void Integer_Destroy(Integer* this);
32.
33.
extern int Integer_CompareIntegers(Object, Object);
34.
extern Object Integer_CopyInteger(Object);
20-11-2014 13:04
30 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
35.
extern void Integer_DestroyInteger(Object);
36.
37.
extern signed int Integer_Value(Integer wrappedInt);
38.
39.
struct _UINTEGER;
40.
typedef struct _UINTEGER* UInteger;
41.
extern UInteger UInteger_Create(unsigned int val);
42.
extern void UInteger_Destroy(UInteger* this);
43.
44.
extern int UInteger_CompareUIntegers(Object, Object);
45.
extern Object UInteger_CopyUInteger(Object);
46.
extern void UInteger_DestroyUInteger(Object);
47.
48.
extern unsigned int UInteger_Value(UInteger wrappedUInt);
49.
50.
struct _LONG;
51.
typedef struct _LONG* Long;
52.
extern Long Long_Create(signed long val);
53.
extern void Long_Destroy(Long* this);
54.
55.
extern int Long_CompareLongs(Object, Object);
56.
extern Object Long_CopyLong(Object);
57.
extern void Long_DestroyLong(Object);
58.
59.
extern signed long Long_Value(Long wrappedLong);
60.
61.
struct _ULONG;
62.
20-11-2014 13:04
31 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
66.
extern int ULong_CompareULongs(Object, Object);
67.
extern Object ULong_CopyULong(Object);
68.
extern void ULong_DestroyULong(Object);
69.
70.
extern unsigned long ULong_Value(ULong wrappedULong);
71.
72.
struct _SHORT;
73.
typedef struct _SHORT* Short;
74.
extern Short Short_Create(signed short val);
75.
extern void Short_Destroy(Short*);
76.
77.
extern int Short_CompareShorts(Object, Object);
78.
extern Object Short_CopyShort(Object);
79.
extern void Short_DestroyShort(Object);
80.
81.
extern signed short Short_Value(Short wrappedShort);
82.
83.
struct _USHORT;
84.
typedef struct _USHORT* UShort;
85.
extern UShort UShort_Create(unsigned short val);
86.
extern void UShort_Destroy(UShort*);
87.
88.
extern int UShort_CompareUShorts(Object, Object);
89.
extern Object UShort_CopyUShort(Object);
20-11-2014 13:04
32 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
90.
extern void UShort_DestroyUShort(Object);
91.
92.
extern unsigned short UShort_Value(UShort wrappedUShort);
93.
94.
struct _DOUBLE;
95.
typedef struct _DOUBLE* Double;
96.
extern Double Double_Create(double val);
97.
extern void Double_Destroy(Double*);
98.
99.
extern void Double_DestroyDouble(Object);
100.
extern int Double_CompareDoubles(Object, Object);
101.
extern Object Double_CopyDouble(Object);
102.
103.
extern double Double_Value(Double wrappedDouble);
104.
105.
struct _FLOAT;
106.
typedef struct _FLOAT* Float;
107.
extern Float Float_Create(float val);
108.
extern void Float_Destroy(Float*);
109.
110.
extern void Float_DestroyFloat(Object);
111.
extern int Float_CompareFloats(Object, Object);
112.
extern Object Float_CopyFloat(Object);
113.
114.
extern float Float_Value(Float wrappedFloat);
115.
116.
#endif
20-11-2014 13:04
33 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
A Sample Module
What follows is an implementation of the wrapper interface for type int. Wrappers for the other primitive
types can be provided similarly and will not be included in this handout.
Integer.c
1.
#include <stddef.h>
2.
#include <stdio.h>
3.
#include <stdlib.h>
4.
5.
#include "Wrappers.h"
6.
7.
struct _INTEGER { signed int value; };
8.
9.
Integer Integer_Create(signed int val) {
10.
Integer this = (Integer) malloc(sizeof(struct _INTEGER));
11.
if (!this) {
12.
fprintf(stderr, "Out of memory...\n");
13.
return NULL;
14.
} /* end of if(!this) */
15.
this->value = val;
16.
17.
return this;
18.
} /* Integer Integer_Create(signed int) */
19.
20.
void Integer_Destroy(Integer* this) {
21.
Integer this_copy = *this;
22.
if (this_copy == NULL) return;
20-11-2014 13:04
34 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
23.
24.
free(this_copy);
25.
*this = NULL;
26.
} /* end of void Integer_Destroy(Integer*) */
27.
28.
void Integer_DestroyInteger(Object this) {
29.
Integer_Destroy((Integer*) &this);
30.
} /* void Integer_DestroyInteger(Object) */
31.
32.
int Integer_CompareIntegers(Object this, Object rhs) {
33.
signed int thisValue = ((Integer) this)->value;
34.
signed int rhsValue = ((Integer) rhs)->value;
35.
36.
if (thisValue == rhsValue) return 0;
37.
else if (thisValue < rhsValue) return -1;
38.
else return 1;
39.
} /* int Integer_CompareIntegers(Object, Object)*/
40.
41.
Object Integer_CopyInteger(Object this) {
42.
Integer retVal = Integer_Create(((Integer) this)->value);
43.
44.
return retVal;
45.
} /* Object Integer_CopyInteger(Object) */
46.
47.
signed int Integer_Value(Integer wrappedInt) {
48.
return wrappedInt->value;
49.
} /* signed int Integer_Value(Integer) */
20-11-2014 13:04
35 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
If you are not convinced you may want to list the contents of the library.
ar t libWrappers.a
Char.o
Double.o
...
...
UShort.o
Now that libWrappers.a has been built, we can now place it in some fixed location and utilize the functions
residing in it as we did in our test program.
Using Windows
Before you issue the following commands make sure command-line tools are made available as external
commands. This may be easily done by executing [the commands contained in] the batch file named
vcvars32.bat found in the bin subdirectory of the Microsoft Visual C/C++ compiler.
cl /ID:/include /c /TC Char.c
cl /ID:/include /c /TC Integer.c
...
...
The preceding lines create the object files we need for building the library. Although rather short, they
underline the fact that we are using a compiler-driver, not a plain compiler. In order to successfully bring in
the required headers, using the /I option we pass the preprocessor information about places to look for
them. Thanks to the /c we also tell the compiler-driver to stop before linking. Finally, not really needed in
our example, using /TC we tell the compiler proper to treat the files passed on the command line as C source
code.
lib /OUT:Wrappers.lib Char.obj Integer.obj ...
20-11-2014 13:04
36 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
Notes
1. With the introduction of inheritance, the same container might end up having items belonging to
different- but compatible- types.
2. Nevertheless, this doesnt mean that we cannot use other techniques.
3. Traditional C allows a variadic function with no named arguments while in Standard C there is a
minimum of one named argument.
4. Note that existing_vec is a local variable and therefore should appear in the lower part of the figure,
just below ap to be exact. The slot marked with existing_vec actually stands for the unnamed formal
parameter that corresponds to the second argument.
5. This can be used as an argument for why a compiler may choose not to evaluate actual parameters
from left-to-right. As a matter of fact, a right-to-left evaluation seems to be a better choice. After all, it
is the rightmost one that gets pushed onto the run-time stack first.
6. While perusing Windows source you may at times see a function name prefixed with specifiers such
as __cdecl (C calling convention), __stdcall (standard calling convention) or __pascal. Such a specifier
is used to tell the type of arrangement, naming convention and cleanup protocol used in the function
call. __cdecl is used with variadic functions, while __stdcall is used for the rest.
7. As a matter of fact, removing the arguments is usually no more than adjusting the stack pointer by a
certain amount.
8. One should also add the genericness provided through void*.
9. If you use a visual tool this would mean adding the object files to your project.
10. Depending on the time of linkage to the executable, a library can be either static or dynamic. The
former brings in the object module at link-time while in the latter this is deferred to the load- or
run-time. Our presentation in this handout will be limited to static libraries.
11. This post-2005 MS Visual Studio command line reflects a non-standard aspect of the Microsoft
compiler: in the name of making C a safer(!) programming language, compiler advisesby means of
warningsthe programmer to use scanf_s, which is a Microsoft-only function, instead of the
standard scanf. We ignore this by defining the _CRT_SECURE_NO_DEPRECATE macro. Starting
with MS Visual Studio 2005, Microsoft has also removed libc.lib- single-threaded static C runtimefrom its distribution. The default C runtime library that is now linked to form the executable is
libcmt.lib, which is the multi-threaded version of libc.lib. However, for some reason, the linker may
still end up looking for libc.lib! In order to fix this glaring glitch, using the /NODEFAULTLIB option,
20-11-2014 13:04
37 of 37
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
we remove libc.lib from the list of libraries searched in the process of resolving external references
and, using /DEFAULTLIB option, add libcmt.lib in its place. Therefore, in a pre-2005 MS Visual
Studio, this command line should be replaced with the following.
cl /ID:/include /FeVector_Test.exe Vector.obj Wrappers.lib
Vector_Test.c /link /LIBPATH:D:/library
Retrieved from "http://en.wikibooks.org
/w/index.php?title=Programming_Language_Concepts_Using_C_and_C%2B%2B/Generic_Containers&
oldid=2710822"
20-11-2014 13:04