Multiple Interfaces And OPF Programming

What does this "interface" mean?

We are talking about implementing something like the interface in Go language, or the trait object in Rust language.

Does OPF Programming support multiple interface?

The skill part of OPF Programming, (*p)->f(p), do not support multiple interface. Because it depends on:

interface pointer have to be the first field of the owner struct.

But can we implement a multiple interface mechanism that is compatible with the OPF Programming?

Yes, we can. And this article describes how we implement it.

Single interface (OPF Programming, for comparison)

Let's define 2 interface: bird_i which contains fly method, and dog_i which contains run and bark method.

typedef int (*bird_fly_fn_t)(void *self, int distance);
typedef int (*dog_run_fn_t)(void *self, int distance);
typedef int (*dog_bark_fn_t)(void *self, int count);
struct bird_i {
    bird_fly_fn_t fly;
};
struct dog_i {
    dog_run_fn_t run;
    dog_bark_fn_t bark;
};

Let's see the OPF Programming implementation (single interface) first.

We define a normal dog species (husky) to implement the dog_i interface:

struct husky {
    struct dog_i *interface;
    const char *name;
};

We define a global object husky_interface which got shared by all struct husky objects.

struct dog_i husky_interface = {
    .run = (dog_run_fn_t)husky_run,
    .bark = (dog_bark_fn_t)husky_bark,
};

The the implementation of interfaces:

int husky_run(struct husky *self, int distance) {
    my_printf("Husky %s is running, target distance: %d\n", self->name, distance);
    return 0;
}

int husky_bark(struct husky *self, int distance) {
    my_printf("Husky %s is barking, target count: %d\n", self->name, distance);
    return 0;
}

Now, let's define a normal bird species (crow) to implement the bird_i interface.

struct crow {
    struct bird_i *interface;
    const char *name;
};

We define a global object crow_interface which got shared by all struct crow objects.

struct bird_i crow_interface = {
    .fly = (bird_fly_fn_t)crow_fly,
};

Then, the implementation of interfaces:

int crow_fly(struct crow *self, int distance) {
    my_printf("Crow %s is flying, target distance: %d\n", self->name, distance);
    return 0;
}

Here is how we use it:

struct husky hus;
dog_run_simple((struct dog_i **)&hus, 10);
struct crow black;
bird_fly_simple((struct bird_i **)&black, 1000);

The dog_run_simple and bird_fly_simple are just wrappers for (*p)->f(p).

int dog_run_simple(struct dog_i **dog, int distance) {
    return (*dog)->run(dog, distance);
}
int bird_fly_simple(struct bird_i **bird, int distance) {
    return (*bird)->fly(bird, distance);
}

The Multiple-interface mechanism (which is compatible with OPF)

In short, the core part of this implementation is a singly linked list who chains all interface objects. There is a Interface Tag inside every node of the list. Some offsets are handled to make this mechanism compatible with OPF Programming.

                                        +--------+         +--------+
                                        |     ---+--.      |     ---+--.
                                        +--------+  |      +--------+  |
  VARIABLE           OBJECT             |  DOG   |  |      |  BIRD  |  |
  +--------+  .--->  +--------+  .--->  +--------+  `--->  +--------+  `--->
  |     ---+--'      |     ---+--'      |  run   |         |  fly   |
  +--------+         +--------+         +--------+         +--------+
                     |        |         |  bark  |
                     |        |         +--------+
                     |        |
                     |        |
                     +--------+

Now let's implement it. First, we define some structs and macros:

struct complex_i_header {
    void *next;
    void *tag;
};

#define H_ADDR_TO_I_ADDR(h) (void *)((uintptr_t)h + sizeof(struct complex_i_header))
#define I_ADDR_TO_H_ADDR(i) (struct complex_i_header *)((uintptr_t)i - sizeof(struct complex_i_header))

#define COMPLEX_INTERFACE(TYPE) \
    struct { \
        struct complex_i_header h; \
        TYPE i; \
    }

Then we define a function to traverse the list and adjust the offsets.

static int find_i_from_chain(void *interface_chain, void *tag, void **result) {
    struct complex_i_header *h;

    h = I_ADDR_TO_H_ADDR(interface_chain);

    if (h->tag == tag) {
        *result = interface_chain;
        return 0;
    }

    if (h->next == NULL)
        return 1;

    return find_i_from_chain(h->next, tag, result);
}

Now, let's define a special dog species (angle dog), who implements bird_i and dog_i in the same time.

struct angel_dog {
    void *interface;
    int wing_span;
    const char *name;
};

With the helper macro, we define 2 global interface object angel_dog_interface_1 and angel_dog_interface_2, which got shared by all struct angel_dog objects.

COMPLEX_INTERFACE(struct bird_i)
angel_dog_interface_1 = {
    .h.next = NULL,
    .h.tag = &bird_i_anchor,
    .i.fly = (bird_fly_fn_t)angel_dog_fly,
};

COMPLEX_INTERFACE(struct dog_i)
angel_dog_interface_2 = {
    .h.next = H_ADDR_TO_I_ADDR(&angel_dog_interface_1),
    .h.tag = &dog_i_anchor,
    .i.run = (dog_run_fn_t)angel_dog_run,
    .i.bark = (dog_bark_fn_t)angel_dog_bark,
};

Now here is the implementation of interfaces:

int angel_dog_fly(struct angel_dog *self, int distance) {
    my_printf("Angel dog %s is flying, target distance: %d\n", self->name, distance);
    return 0;
}

int angel_dog_run(struct angel_dog *self, int distance) {
    my_printf("Angel dog %s is running, target distance: %d\n", self->name, distance);
    return 0;
}

int angel_dog_bark(struct angel_dog *self, int distance) {
    my_printf("Angel dog %s is barking, target count: %d\n", self->name, distance);
    return 0;
}

Let's define 2 demo functions to use the multiple-interface objects:

int bird_fly_complex(void *bird, int distance) {
    struct bird_i *i;
    if (find_i_from_chain(*(void **)bird, &bird_i_anchor, (void **)&i))
        return 1;

    return i->fly(bird, distance);
}

int dog_run_complex(void *dog, int distance) {
    struct dog_i *i;
    if (find_i_from_chain(*(void **)dog, &dog_i_anchor, (void **)&i))
        return 1;

    return i->run(dog, distance);
}

Usage example:

struct angel_dog air;

//...

dog_run_complex(&air, 100);
bird_fly_complex(&air, 2000);

The first interface in the chain is struct dog_i, so you can use OPF Programming on it (the compatibility):

dog_run_simple((struct dog_i **)&air, 10);

But ...

It's not suggested to use this skill in serious programs. This will be explained in the future.