libooc/example/animal.h
2026-10-02 00:08:18 +02:00

107 lines
3.4 KiB
C

/*
* This file is part of libooc.
* https://xw3.org/hanez/libooc
*
* Copyright 2026 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*/
#ifndef ANIMAL_H
#define ANIMAL_H
#include <ooc/ooc.h>
typedef struct Animal Animal;
typedef struct AnimalVTable AnimalVTable;
/*
* Virtual methods inherited by every animal.
*
* The vtable is a plain struct of function pointers, so dispatch is an ordinary
* call through a pointer and needs no support from the compiler. A subclass
* fills in its own table and installs it in the instance, which is how `speak`
* reaches Dog's implementation when the object is only known as an Animal.
*
* Each pointer is called with the object as its first argument, so an
* implementation casts it back to the class it belongs to.
*/
struct AnimalVTable {
void (*speak)(Animal *self);
};
/*
* Base class.
*
* Every instance starts with an ooc_object header, which the runtime needs
* for reference counting and the virtual destructor. Subclasses embed this
* struct as their first member, which makes the upcast free.
*/
struct Animal {
ooc_object object;
const AnimalVTable *vtable;
char *name;
int age;
/*
* Two fields that belong to the class alone, told apart by their leading
* underscores.
*
* `_id` is published, so it can be read by name, and ooc_set() refuses to
* write it. `__legs` is hidden outright: ooc_get() withholds it as well, so
* nothing outside Animal can read or write it by name at all. Both are set
* from the struct definition below, where the members are in view.
*/
int _id;
int __legs;
};
/*
* Animal's runtime type record, the value ooc_new() takes.
*
* A subclass names it in its own `super`, which is how field lookup continues
* up the chain and how Dog's objects still resolve "name" and "age".
*/
extern const ooc_class Animal_class;
/*
* Create an Animal named `name` and return it, or NULL.
*
* Ownership of the result is the caller's, with a reference count of one, so it
* has to reach ooc_release() eventually. Returns NULL if the allocation or the
* copy of the name fails, in which case there is nothing to release.
*/
Animal *animal_new(const char *name, int age);
/*
* Read back `_id`, the field ooc_get() also returns.
*
* Takes a const object, so it can be called where ooc_get() could not.
*/
int animal_id(const Animal *animal);
/*
* Read back `__legs`, the field ooc_get() withholds.
*
* This accessor is the only way to learn the value, which is the point of a
* two-underscore name. Returns 0 for a NULL object.
*/
int animal_legs(const Animal *animal);
/*
* Initialise the members Animal owns, so a subclass constructor can build the
* base part without duplicating it. Requires zero-initialised member storage
* and external synchronization between constructors in different threads.
* Returns 0, or -1 for NULL, an already initialised animal, exhausted positive
* int IDs, or a failed name copy. Failure leaves the animal unchanged.
*/
int animal_init(Animal *animal, const char *name, int age);
/*
* Dispatch `speak` through the vtable, so the implementation depends on the
* object's runtime type. A NULL object, a NULL vtable and a NULL method are all
* tolerated and do nothing.
*/
void animal_speak(Animal *animal);
#endif /* ANIMAL_H */