Added garfield and snoopy classes to ./example which are derived from the cat and dog classes.
This commit is contained in:
parent
6ca258f882
commit
32e7d0b337
13 changed files with 1519 additions and 69 deletions
122
example/garfield.h
Normal file
122
example/garfield.h
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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 */
|
||||
Loading…
Add table
Add a link
Reference in a new issue