SoC Logo HowTo
Use Object Oriented Coding Techniques in C Prof. Bartenstein
 

Contents:

Overview

Why Use Object Oriented Techniques in C?

The C language is an imperative language which pre-dates the concepts of object orientation. When it became clear that object oriented techniques brought a new level of rigor and precision to programming, the C++ language was invented to fully implement an object oriented language derived from C. However, I find that C++ is a very difficult language to code in. It's just too easy in C++ to write code that intuitively seems correct, but which has hidden bugs.

One alternative is to use several of the object oriented techniques without using all of the complexity introduced by C++; instead, sticking with the simple C language, plus an object oriented discipline that enables many of the advantages of a full object oriented implementation without the complexity.

arrow_circle_up

Object Oriented Concepts Implemented in C

In object oriented thinking, the things our program works with can be thought of as on object in a specfic class. A class consists of a definition of fields, the data that describes an object; and methods, the actions that can be performed on that object.

A class typically has a level of encapsulation. Most often, users of the class can invoke methods in that class, but do not have direct access to the fields in the class. All methods and field definitions are collected (encapsulated) in a single file used to define the class, managed by the owner of that class. Furthermore, classes become a specific data type, and we can declare reference variables using that data type to point to instances of objects in that class.

New objects in a class are often created by a creator method, and deleted by a delete method. If the coder of a class wants to allow users to read or write specific fields, she will often provide getter and setter methods for those fields as well.

When coding in C, we can use techniques to mimic these concepts, as follows:

Class
A class can be encapsulated in two files; class_name.c and class_name.h, based on the standard C convention to package a set of functions in a file to be used by other C code. The name.h file is a "header" file that contains all of the information required to use the functions. This file will be included by all C code that invokes the functions. The name.c file will contain the definitions of the functions themselves. For more details on header and code files, see the How To write a C program using multiple files web page.

In a C object oriented environment, the class_name.h file will contain declarations for all methods in the class, and a typedef to create a data type for that class. The class_name.c file will contain a structure definition that defines all of the fields for the class, and function definitions for all of the methods in the class.

Fields
Fields are defined as members of a structure declared in the class_name.c file. By convention, the name of this structure will be class_name_struct. The fields or members of this structure can be any valid C declaration, including built-in data types like int or float; arrays and/or pointers like char*; and "reference" variables which are pointers to other objects.
Methods
A list of functions that work on the object, declared in class_name.h, and defined in class_name.c.

In a true object oriented language, you can use the same method names for different classes. The object oriented infrastructure chooses which method to invoke based on the reference variable that invokes the method. When using object oriented techniques in a simple language like C, no such infrastructure exists. Therefore, we must make each method name unique across all classes. The easiest way to accomplish method name uniqueness is to include the class name as part of the method name. For example instead of coding a move method in the ball class, we will code a ball_move class in the ball class. Then, we know it has a different method name than (for example) the bat_move method in the bat class.

Encapsulation
We create a typedef that specfies a pointer to our class structure in the publicly available class_name.h file, but we do not include the structure definition. This allows us to create a reference variable, a pointer to an instance of the class structure, without defining the fields in the structure. Code which uses the class (by including the class_name.h file) can create references to our "objects", but does not have access to the fields in an object! Users of the object must use getter or setter methods to get or set specific field values.

The capability to create a reference to a structure that has not yet been defined is called forward declaration in C. A forward declaration allows us to create a pointer to a structure without knowing the details about that structure, along with the promise that by the time we use that pointer to reference specific fields in the structure, the definition of the structure will be known.

The class data structure which defines the fields is coded in the class_name.c file. The result is that only the code in the class_name.c file can access the fields in that structure.

Using a forward declaration in a typedef of a pointer to an undefined structure is called an Abstract Data Type, and it provides encapsulation without a complex object oriented infrastructure.

Object
An object is just a specific instance of the class structure. Since users of the class have no access to the class structure, they depend on the creator method in the class to create a new object and return a reference to that object using the class data type.
Class Data Type
The data type associated with a class is implemented by a typedef in the class_name.h file. The typedef typically defines the class name as a pointer to the structure which contains the fields. For example, the ball class would have a line in the ball.h file:

	typedef struct ball_struct * ball;
This defines the ball type as a pointer to an instance of the structure ball_struct.
Reference Variable
This is a variable declared with the class data type. Under the covers, this is a pointer to an instance of the structure associated with the class.
Creator Method
By convention, we will call the primary creator method usnig a function called class_name_create. The parameters to this function will specify the values needed to create the object. This function will be declared in class_name.h and defined in class_name.c. It will malloc space for an instance of the class data structure: class_name_struct, initialize the fields in that structure, and return a pointer to the structure which can be used as a reference to that object.

You can code extra creators with different inputs, but they must have unique function names (like class_name_create_fromSphere) that has different parameters, but performs the same functions.

Delete Method
By convention, we will call the delete method using a function called class_name_delete. This function should have a single parameter, a reference to the object to delete. The function should return void. This function will be declared in class_name.h and defined in class_name.c. It should invoke free for the parameter pointer to the class structure after taking care of any allocations made for the object (if any.)
Getter Methods
If you want to provide read access to fields in your class, code methods that are named (by convention) class_name_get_field_name that take a single parameter - a reference to an object, and return the value of the field. These functions are declared in class_name.h, and defined in class_name.c.
Setter Methods
If you want to provide write access to fields in your class, code methods that are named (by convention) class_name_set_field_name that take a two parameters - a reference to the object, and the new value for the field. Setter functions by convention return void. These functions are declared in class_name.h, and defined in class_name.c.
Other Methods
There can be other methods in the class, defined by functions whose name is prefixed with the class name, and whose first parameter is an object reference variable. These methods must be declared in the class_name.h file, and defined in the class_name.c file.

arrow_circle_up

Object Oriented Concepts Not Implemented

The techniques described on this web page allow you to do some object oriented style programming using simple C code, but obviously you can't do everything you could do with a full object oriented language like C++. The following is an incomplete list of the things you can't do with these techniques:

arrow_circle_up

Writing Object Oriented C Code

Object Oriented Design

Object oriented design using the techniques is very similar to object oriented design in a true OO language. You need to decide what classes you will need, and for each class, what are the fields and methods required for that class. Of course, without inheritance, you won't need to worry about parent and child classes, but you can still get a lot done without inheritance.

arrow_circle_up

Coding the class_name.h File

See the How To Write a program using Multiple Files web page for details on coding a generic header file. We need to include at least two things in the object oriented C class header file: a typedef to define the type associated with the class, and method declarations. The typedef simply defines the class as a pointer to the class structure. For the ball class, this would be:


typedef struct ball_struct * ball;

The method declarations are relatively straightforward function declarations. For the ball class, they might be something like:


ball ball_create(float radius,color ballColor);
float ball_get_radius(ball b);
color ball_get_color(ball b);
void ball_set_radius(ball b,float newRadius);
void ball_set_color(ball b,color newColor);
void ball_delete(ball b);

So, the entire ball.h header file might look something like:


#ifndef BALL_H
#define BALL_H
#include "color.h" // My "color" object to keep track of colors

typedef struct ball_struct * ball;

ball ball_create(float radius,color ballColor);
float ball_get_radius(ball b);
color ball_get_color(ball b);
void ball_set_radius(ball b,float newRadius);
void ball_set_color(ball b,color newColor);
void ball_delete(ball b);

#endif

arrow_circle_up

Coding the class_name.c File

See the How To Write a program using Multiple Files web page for details on coding a generic code file. The class_name.c file should start with any #includes required, including including the class header file, and header files for any other parts of your project or system functions used in the implementation of the methods. For example, for the ball class, you might have:


#include "ball.h"
#include <stdlib.h> // for malloc and free
#include <stdio.h> // for debug messages

Next, we need to define the class structure which contains all the fields. For the ball class, this might look like:


struct ball_struct {
  float radius;
  color color;
}

Next is the creator method. Here's an example:


ball ball_create(float radius,color ballColor) {
  ball b = malloc(sizeof(struct ball_struct));
  b->radius=radius;
  b->color=ballColor;
  return b;
}

Getter and setter methods are pretty much one-liners, e.g.:


float ball_get_radius(ball b) { return b->radius; }
void ball_set_radius(ball b,float newRadius) { b->radius=newRadius; }

Obviously, you will be coding more complicated methods that do much more than this simple code, but you get the idea. Finally, the delete method code is very straightforward for the simple ball class:


void ball_delete(ball b) { free(b); }

Note that if the object itself allocated any memory, the delete code would free that memory before freeing up the structure instance itself.

arrow_circle_up

Object Oriented C References

There's not much about these techniques, but check out the Wikipedia article on Forward Declaration and Abstract Data Type. Also, here's a slightly different take from Software Engineer Dmitry Frank, using slightly different techniques: Object-oriented tehcniques in C. And a University of Washington web page titled Ojbect Oriented C Programming.

arrow_circle_up