Notes for C
C
1. Functions are named "Az" + class name + "_" + function name in pascalCase. Modules / namespaces do not exist:
app::App::new() = AzApp_new()
2. Enums are named "Az" + enum name + "_" + variant name:
LayoutAlignItems::Stretch = AzLayoutAlignItems_Stretch
3. union enum
s have special compile-time macros that crate the correct union variant + tag at compile time:
AzStyleCursorValue cursor = AzStyleCursorValue_Exact(AzStyleCursor_Grab);
4. If a class is marked with has destructor, it has a corresponding _delete()
function. The destructors automatically call the sub-destructors of all fields (i.e. you do not need to recurse and delete every field manually).
# App is marked as "has_destructor":
AzApp app = AzApp_new(/* ... */); # constructor
AzApp_delete(&app); # destructor
# value of app is undefined here
5. All classes can be deep-copied via _deepCopy()
- note that this might be very expensive for large objects.
AzWindowCreateOptions w = AzWindowCreateOptions_new();
AzWindowCreateOptions copy = AzWindowCreateOptions_copy(&w);
6. To emulate pattern matching in C, every union enum
has a corresponding $union_matchRef$variant()
and $union_matchMut$variant()
function.
Both take a pointer to the union and an (uninitialized) pointer to the output. If the union is of variant $variant, the output pointer will be initialized:
// create a union enum
AzStyleCursorValue cursor = AzStyleCursorValue_Exact(AzStyleCursor_Grab);
// destructure a union enum
AzStyleCursor* result;
if AzStyleCursorValue_matchRefExact(&cursor, &result) {
printf("ok!\n");
// result is initialized here
}
The difference between
_matchRef()
and _matchMut()
is that takes a const *
and _matchMut()
takes a restrict *
to the result. In the example, the lifetime of result
is equal to the lifetime of
cursor
(since result
simply points to the payload of the tagged cursor
union).7. Run-time type reflection for RefAny
can implemented via the AZ_REFLECT
macro:
typedef struct { int field; } MyStruct;
void MyStruct_delete(MyStruct* restrict A) { } // destructor
AZ_REFLECT(MyStruct, MyStruct_delete);
The AZ_REFLECT
macro generates functions to upcast from your struct to a
RefAny
as well as to do a checked downcast from a RefAny
to your custom data type:
// create a new RefAny
AzRefAny object = MyStruct_upcast(MyStruct { .field = 5 });
// Creates an uninitialized borrow and downcast it
// fails if "object" is already borrowed mutably
// or if the "RefAny" is not of type "MyStruct" (type check at runtime)
MyStructRef structref = MyStructRef_create(&object);
if MyStruct_downcastRef(&object, &structref) {
printf!("ok! - %d", structref->ptr.field);
}
// unlocks "object" to be borrowed mutably again
MyStructRef_delete(&structref);
// Creates a borrow that can MODIFY the contents of the RefAny
// (mutable "* restrict" borrow instead of "const*" borrow)
//
// The object cannot be borrowed referentially and mutably at
// the same time - this invariant is checked at runtime.
MyStructRefMut structrefmut = MyStructRefMut_create(&object);
if MyStruct_downcastRefMut(&object, &structrefmut) {
// structrefmut can modify the contents of the RefAny
structrefmut->ptr.field = 6;
// prints "6", value is now changed, visible to all copies of "object"
printf!("ok! - %d", structref->ptr.field);
}
MyStructRefMut_delete(&structrefmut);
// decreases the refcount - if refcount is 0, destructor is called
if !MyStructRefAny_delete(&object) { /* RefAny is not of type "MyStruct" */ }
8. All *Vec
data types have a _empty()
constructor macro (to create an empty vec at compile time).
The AzString
type has an extra _fromConstStr()
macro to define compile-time strings:
AzScanCodeVec v = AzScanCodeVec_empty();
AzString s = AzString_fromConstStr("hello"); // evaluated at compile-time