1. Every type implements __str__()
and __repr__()
for debug printing
2. Simple (non-union) enum types implement__richcmp__()
:
align = LayoutAlignItems.Stretch
if align == LayoutAlignItems.Stretch:
print("ok!")
3. union enums
have a @staticmethod
constructor and a hidden .match()
function that will return the enum tag as a string and the enum payload as a PyObject:
size = OptionLogicalSize.Some(LogicalSize(600, 800))
tag, payload = size.match()
if tag == "Some":
print("size is {}, {}".format(payload.width, payload.height))
elif tag == "None":
print("no size available")
4. new()
constructors are the default constructors:
dom = Dom(NodeType.Div) # not Dom.new(NodeType.Div)!
5. If no explicit new()
constructors exist, the default constructor takes all arguments in the order as they are specified in the API:
# API: struct ColorU { r: u8, g: u8, b: u8 a: u8 }
# therefore the arguments to the default constructor are passed in order:
color = ColorU(/*r*/ 255, /*g*/ 255, /*b*/ 255, /*a*/ 255)
6. Whenever a RefAny
parameter is required to call a function, you can use any PyObject as a replacement:
mydata = MyClass() # your custom data type
# App::new() takes a RefAny as the first argument - pass in your custom data type here
app = App(mydata, AppConfig(LayoutSolver.Default))
7. All functions that take a *Vec
type accept a PyList and all *Option
types are automatically converted to and from Pythons None
value:
monitors = app.get_monitors() # returns MonitorVec = PyList[Monitor]
print(str(monitors[0]))
# returns OptionLogicalPosition = Optional[LogicalPosition]
cursor_relative = callbackinfo.get_cursor_relative_to_viewport()
if cursor_relative is not None:
print(str(cursor_relative))
8. Due to a bug in the Python PyO3 bindings, you cannot modify a struct through a struct (see issue).
window = WindowCreateOptions(LayoutSolver.Default)
window.state.flags.frame = WindowFrame.Maximized # does not work
# workaround (yes, it's annoying):
state = window.state.copy()
flags = state.flags.copy()
flags.frame = WindowFrame.Maximized
state.flags = flags
window.state = state
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
}
_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
1. Like in Python, default constructors of classes take the arguments in the order or the fields:
// API: struct ColorU { r: u8, g: u8, b: u8 a: u8 }
// therefore the arguments to the default constructor are passed in order:
auto color = ColorU(/*r*/ 255, /*g*/ 255, /*b*/ 255, /*a*/ 255);
2. Explicit constructors are static functions, enums use enum class
(C++11):
auto window = WindowCreateOptions::default(LayoutSolver::Default);
3. All by-value arguments require std::move
in order to prevent accidental copies:
auto window = WindowCreateOptions::default(LayoutSolver::Default);
app.run(std::move(window));
4. In difference to C, constructing a RefAny
does not require macros, instead generics are used:
class MyStruct {
int field;
public:
MyStruct(int f) noexcept: field(f) { }
}
auto object = RefAny::new(std::move(MyStruct(5)));
auto objectref = object.downcastRef<MyStruct>();
if (objectref): // objectref = std::optional<Ref<MyStruct>>
std::cout << objectref->ptr.field << std::endl;
objectref.delete(); // release reference
auto objectrefmut = object.downcastRefMut<MyStruct>();
if (objectrefmut):// objectrefmut = std::optional<RefMut<MyStruct>>
std::cout << objectrefmut->ptr.field << std::endl;
objectrefmut.delete(); // release reference
// "object" RefAny destructor automatically decreases refcount here
5. All *Vec
types have a _fromStdVector()
function
that converts a std::vector
into the given Vec
type without copying the contents of the array.
Additionally a _fromStdString
function exists
to convert a std::string
into a String
type. All strings are UTF-8 encoded.
auto scancodes = std::vector{0,1,2,3};
auto converted = ScanCodeVec::fromStdVector(std::move(scancodes));
auto converted_back = converted.intoStdVector();
6. Like in C, all union enums
have special functions to emulate pattern matching:
// create a union enum - Exact() is a constexpr function
auto cursor = StyleCursorValue::Exact(StyleCursor::Grab);
// destructure a union enum
if (auto result = cursor.matchRefExact()) {
// result = const StyleCursor*
std::cout << result << std::endl;
}
if (auto result = cursor.matchRefMutExact()) {
// result = StyleCursor* restrict
*result = StyleCursor::Default;
}