libooc/example/garfield.h

122 lines
No EOL
4.9 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 GARFIELD_H
#define GARFIELD_H
#include "cat.h"
typedef struct Garfield Garfield;
/*
* A subclass of a subclass, three levels below the root.
*
* The embedded Cat comes first, so the same two properties hold one level down
* from the case dog.h describes: the ooc_object header stays at offset zero
* where the library looks for it, and the upcast from Garfield * to Animal * is
* a no-op cast because every address is the same. The cost is unchanged too --
* no member may go before the base.
*
* Garfield adds a favourite food of its own and nothing that shadows Cat or
* Animal, so every inherited field keeps the meaning it had. "colour" and
* "_lives" still resolve to Cat's storage, and the underscore rules still apply
* to the class that declared them: a caller can read "_lives" by name and cannot
* write it, even though Garfield sits between the caller and the declaration.
*
* The struct holds no destructor of its own; the one Garfield registers lives in
* Garfield_class and releases all three strings in the hierarchy -- the food,
* Cat's colour and Animal's name.
*/
struct Garfield {
Cat cat;
/*
* Garfield's own field, and the only one it adds. Marked as owned in the
* field table, so ooc_set() releases the string it displaces exactly as it
* does for a Dog's breed or a Cat's colour. A third level of subclassing
* changes nothing about how ownership behaves.
*/
char *favourite_food;
/*
* A field nobody but this class may change, counted in meals rather than
* lives. One underscore hides it from ooc_set() and leaves it readable, and
* garfield_meals() is the way a caller asks politely.
*
* Worth noting that `Cat` has a `_lives` of its own and Garfield adds a
* second private field rather than reusing it. Private means private to the
* declaring class, so a subclass cannot lean on the base class' private
* state even from inside the same hierarchy -- which is why the two fields
* coexist under different names.
*/
int _meals;
};
/*
* Garfield's runtime type record, the value ooc_new() takes.
*
* Its `super` is Cat_class rather than Animal_class, which is the whole of a
* third level: field lookup starts here, continues at Cat_class for "colour" and
* "_lives", and reaches Animal_class for "name", "age", "_id" and "__legs". The
* chain is walked as far as it goes, so nothing from an ancestor is lost by
* inserting a class in between.
*
* Its `size` is sizeof(Garfield), which has to hold Cat and Garfield's own
* member on top, and the runtime rejects a class smaller than its base.
*/
extern const ooc_class Garfield_class;
/*
* Create a Garfield named `name`, of the given `colour` and `favourite_food`,
* and return it, or NULL.
*
* The result has a reference count of one, so ownership is the caller's and it
* must reach ooc_release() eventually. Returns NULL if any step of the
* construction fails, leaving nothing to release.
*
* The object is a Garfield and answers to Garfield's `speak` however it is later
* reached -- through a Garfield *, a Cat * or an Animal * alike.
*/
Garfield *garfield_new(const char *name, int age, const char *colour,
const char *favourite_food);
/*
* Initialise the members Garfield owns, so a subclass of Garfield could build
* this part for itself.
*
* The third link in the chain of constructors: garfield_init() composes
* cat_init(), which composes animal_init(). Each one installs its own vtable and
* the one above overwrites it, so the object ends up speaking as the most
* derived class.
*
* The caller supplies storage that ooc_new() has already allocated and zeroed.
* On failure the object is left exactly as it was found, so the caller may
* release it without a destructor having anything to do.
*
* Returns 0, or -1 for a NULL object, an already initialised one, or a failed
* copy. Requires zero-initialised members and external synchronization between
* constructors in different threads.
*/
int garfield_init(Garfield *garfield, const char *name, int age,
const char *colour, const char *favourite_food);
/*
* Read back `_meals`, the field ooc_get() also returns.
*
* The accessor a class provides for a value it keeps to itself. A single
* underscore leaves the field readable by name, so this is a courtesy rather
* than a necessity, unlike animal_legs() for the two-underscore `__legs`.
*
* Returns 0 for a NULL object, which is indistinguishable from a real count of
* zero, so a caller that has to tell the two apart should check the pointer
* first.
*/
int garfield_meals(const Garfield *garfield);
#endif /* GARFIELD_H */