What is OPF Programming?

Introduction

OPF (Object Pointer Forwarding) Programming is a programming skill for low-level languages like C language.

It can also be represented by:

(*p)->f(p)

Before explanations, we would like to show this memory layout graph first:

  Variable           Object             Interface (Function Pointers)
  +--------+  .--->  +--------+  .--->  +--------+
  |     ---+--'      |     ---+--'      |   f1   |
  +--------+         +--------+         +--------+
                     |        |         |   f2   |
                     |        |         +--------+
                     |        |
                     |        |
                     +--------+

This graph has shown how (*p)->f(p) works.

The aim of this skill, is to provide a Top-Down programming way, which is the key to build huge program.

Programs designed in the Bottom-Up style:

We implement A, then implement B who is based on A.

Programs designed in the Top-Down style:

We implement B first, then someone else implement A to finish our B.

So:

How can we finish B when B depends on A and A does not exist yet?

OPF make it possible.

Example

Say we want to write a set of function like draw_rect, draw_circle, which support different printing targets.

How to do that? If we have a generic draw_point, it would be easy to implement those function.

So the problem got converted into:

How to implement a generic draw_point.

With the Point Five skill, we write a interface struct like this:

typedef int (*painter_draw_point_fn_t)(void *self, struct point pos, int color);

struct painter_i {
	painter_draw_point_fn_t draw_point;
};

Now, here comes the magical part: We can implement the drawing function without any real screen drivers!

int draw_rect(struct painter_i **painter, struct point p1, struct point p2,
		int color)
{
	struct point c;
	/* ... */
	for (/* get the next point to draw and store the point in c */) {

		/* (*p)->f(p) */
		(*painter)->draw_point(painter, c, color);


	}
	/* ... */
	return 0;
}

This is the Top-Down design.

Now let's implement a RGB screen driver:

struct rgb_screen {
	struct painter_i *interface;	/* has to be the first field. */
	int color_type;
	int color_mask;
	/* ... */
};
int rgb_screen_draw_point(struct rgb_screen *self, struct point pos, int color)
{
	/*
	 * Draw point on the RGB screen,
	 * `self->color_type` and `self->color_mask` will be used here.
	 */
}

struct pointer_i rgb_screen_interface = {
	.draw_point = (painter_draw_point_fn_t)rgb_screen_draw_point,
};

int rgb_screen_init(struct rgb_screen *self)
{
	self->interface = &rgb_screen_interface;
}

Then let's implement a mono screen driver:

struct mono_screen {
	struct painter_i *interface;	/* has to be the first field. */
	unsigned char bit_buffer[1024 * 768];
	/* ... */
};
int mono_screen_draw_point(struct mono_screen *self, struct point pos,
		int color)
{
	/*
	 * Draw point on the mono screen,
	 * `self->bit_buffer` will be used here.
	 */
}

struct pointer_i mono_screen_interface = {
	.draw_point = (painter_draw_point_fn_t)mono_screen_draw_point,
};

int mono_screen_init(struct mono_screen *self)
{
	self->interface = &mono_screen_interface;
}

Now here is how we use the code:

struct rgb_screen scr1;
struct mono_screen scr2;

draw_rect(&scr1, p1, p2, RED);
draw_rect(&scr2, p1, p2, WHITE);

draw_circle(&scr1, p1, 10, RED);
draw_circle(&scr2, p1, 10, RED);

You may feel OPF Programming a little bit complex, and don't understand why those things are done.

But when we get more familiar with this skill, we can construct the whole thing inside our head without effort.

Rules and Styles

The core concept of OPF do not have coding rules, but a consistent coding rule can make collaborating easier.

After some practice, some simple rules are collected:

  1. The 1st parameter should be the object pointers, and should be named self.
  2. Interface structs should be named with suffix _i. (e.g. struct iter_i)
  3. Function only return status code, results are passed out though pointers.
  4. Follow the Linux kernel coding style.

Explanations for the 3th Rule

Many C code return the result directly, and return error code by pointer.

e.g.

int fn(int a, int b, int *error)
{
	if (some_check()) {
		*error = CODE_xxx;
		return SOMETHING;
	}

	return a + b;
}

But the OPF Programming use the following style:

int fn(int a, int b, int *result)
{
	if (some_check())
		return CODE_xxx;

	*result = a + b;
	return 0;
}

Error code are returned, and result is return through argument pointer. Returning 0 means success.

Some coding styles return booleans, which means 1 means success, this is different from OPF Programming.

Explanations for the 4th Rule

There are 2 reasons for choosing Linux kernel coding style:

Since we use Linux kernel coding style, variables, functions and types are all named in snake case (like a_b_c).

Camel case (like aBc) or Pascal case (like Abc) is not used.

Iterator (Another example)

The OPF Programming uses many iterators, the iterator itself is an application of OPF Programming, but we pick it out and describe it since it's widely used.

In OPF Programming, we only need to implement 2 interfaces:

_iter_init and _iter_next.

When we use it, code should look like this:

int blah(void)
{
	struct xxx_iter iter;
	struct element *tmp;
	size_t index;


	if (xxx_iter_init(&iter))
		return XXX_ITER_INIT_failed;

	while (!xxx_iter_next(&iter, &tmp, &index)) {
		if (do_something_on_element(tmp))
			return XXX_ITER_ELE_ERROR;
	}

	return 0;
}

Suggestions

The OPF Programming style is like Boxing in the programming paradigm world. It contains only a few rules, but we can solve huge problem by combing those simple rules.