-
Notifications
You must be signed in to change notification settings - Fork 19
General tips and recommendations
The error handling in PawnPlus isn't meant to emulate a try/catch system. Instead, when an error happens, it comes from wrong usage of functions in the script. Using wrong indices, nonexistent objects, or unsupported operations will lead to an error that could have been prevented with more thorough checks.
Guards represent a way to ensure automatic cleanup of objects used in the current scope.
new List:l = list_new();
task_await(CreateSomeTask());
list_delete(l);
It is possible the task created from CreateSomeTask()
gets deleted by some external code before it manages to be finished, in which case list_delete
never gets called, causing a memory leak. Using a guard is the best way to ensure the list is always cleaned up (assuming it is meant to be temporary).
new List:l = list_new();
pawn_guard(l);
task_await(CreateSomeTask());
Even when the task gets deleted early, the guard ensures the object is destroyed.
Handles are a more elaborate mechanism for managing lifetime of complex objects, and are especially useful when sharing objects. A handle allows you to observe the lifetime of an object, and also to destroy it when the handle itself gets deleted. Handles can also be combined with guards:
stock AsyncProcess(Handle:lh)
{
task_detach();
pawn_guard(lh);
new List:l = List:handle_get(lh);
task_await(CreateSomeTask());
//...
}
new List:l = list_new();
new Handle:lh = handle_new(lh);
AsyncProcess(lh);
This guarantees all functions that need to work with a specific object acquire the handle (via pawn_guard
) and thus prevent the object from being destroyed, but if all functions terminate (releasing the handle each time), the list gets automatically destroyed.
A variant function is a function designed for accepting values of different tags and types. Such a function usually comes in 4 overloads ending with a specific suffix that indicates the type of values it accepts: nothing, _arr
, _str
, _var
.
native Variant:var_new(AnyTag:value, TagTag:tag_id=tagof(value));
native Variant:var_new_arr(const AnyTag:value[], size=sizeof(value), TagTag:tag_id=tagof(value));
native Variant:var_new_str(const value[]);
native Variant:var_new_var(ConstVariantTag:value);
The first overload accepts a single cell, which is used to create a cell variant (immutable). The second overload copies the value of an array into a mutable variant. The third overload computes the length of the string and creates a variant with a special tag (tag_uid_char
) to indicate it's a string. The fourth overload copies the value of the variant represented by value
.
AnyTag
is a macro that expands to a list of generally known tags. tag_id
accepts the result of the tagof
Pawn operator, but also a tag_uid
.
The _str
version may also be accompanied with a _str_s
version which copies the value from a dynamic string.
For containers which store values of any tags, it is necessary to provide a tag-safe way of obtaining the value. Variant functions which obtian a value are accompanied with a _safe
version that checks the tag of the output variable:
native bool:var_get_safe(ConstVariantTag:var, &AnyTag:value, offset=0, TagTag:tag_id=tagof(value));
native var_get_arr_safe(ConstVariantTag:var, AnyTag:value[], size=sizeof(value), TagTag:tag_id=tagof(value));
native var_get_str_safe(ConstVariantTag:var, value[], size=sizeof(value));
native String:var_get_str_safe_s(ConstVariantTag:var);
The version for single cells returns a bool indicating if the tags match. The array and string versions return 0, and the str_s
version returns STRING_NULL
.
new Variant:v = var_new(3.14);
new Float:value;
assert var_get_safe(v, value);