Class basics

Homepage

Table of Contents

Class values

See Class values for detailed information on the "class values" which are included as part of every class object.

ClassObject is an address?

Yes. Class objects are merely overglorified memory addresses that Class.ahk applies some magic on and we have Classes.

What this means is that you don't have to worry about passing an object ByRef, and you can store them anywhere you could store a number.


For advanced users: This means, for example, they can be associated with API menus, as my menu wrapper library does, since there is a 4 byte value, dwItemData, which can be used to store the ClassObject. There is a lParam value in the TVITEM structure that could be used to associate a class object with a TreeView item, and I presume other controls have a similar field - the sky is the limit.


So, simply put, because it's a number, it can go almost anywhere. However, please do not try to do math with it, it's more than JUST a number after all.

Types

In addition to the basic types: String, Int64, UInt64, Int, UInt, Short, UShort, Char, UChar, Float, and Double, there are some additional ones.

Note: when using the Class getters and setters, if no type is specified, the default, uint will be used.


Object types

obj (object): Instructs the library to treat the stored value as an object.

Used in Getters: "Unwraps" wrappers, and returns the address for non-wrappers. Note: to return the address of an object for wrappers and non-wrappers alike, use uint instead (see Obj vs Uint for details).

Used in Setters: Destroys the old object and increases the lock count for the set object.

For Equals: Performs a deep (recursive) comparison of the objects.

For Clone: A shallow copy will create a new "shell" for the object, filling it with copies of the object's values. In a shallow copy, only the address for "obj" types is copied. For a deep copy, a recursive clone is created. In a settings-only copy, the local clone function is responsible for setting the value.

On destroy: The object's lock count is decreased by one. If the new lock count is zero, the object is destroyed, and NULL (0) is set in its place. Else, the object is not destroyed, and remains valid to be used by it's other references.


robj (replace object): Used only in setters. Instructs the library to only replace the previous object (and not destroy it). The previous object's address is returned. 0 is returned if the the value was unset.


Pointer types

Pointer types are uints with a little extra umph. They instruct the library that the value stored is a pointer to an object.


ptr (pointer):

For Equals: Performs a deep (recursive) comparison of the objects.


sptr (shallow pointer):

For Equals: Compares the pointer values - does not compare the actual object.


Both pointer types:

Used in Getters: "Unwraps" wrappers, and returns the address for non-wrappers. Note: to return the address of an object for wrappers and non-wrappers alike, use uint instead (see Obj vs Uint for details).

Used in Setters: Sets the value to the address of the object. Note, the lock count for the object is not increased. Use "obj" instead to treat the value as an object.

For Clone: Copies the pointer over to the clone.

On destroy: Sets the value to NULL (0).


String types

String (case-insensitive string):

For Equals: Compares the string values using a case-insensitive equal (=). See Variables and Expressions -> equal for details.


SString (case-sensitive string):

For Equals: Compares the string values using the case-sensitive equal (==). See Variables and Expressions -> equal for details.


Both string types:

Used in Getters: Returns the stored string value.

Used in Setters: Sets the string value, returning the previous string.

For Clone: Copies the string over to the clone.

On destroy: Sets the value to NULL (0).


Getters and Setters

Getter and setter functions are the "entry points" into storing and extracting an object's values.

Use the class library's getters and setters to set and retrieve values in your class objects.

For all types except obj, when setting a value, the previous value is returned.

Obj vs UInt

When getting an "obj" value or a "ptr" / "sptr" value, it is sometimes desired to return the address for the value (even for wrappers).

To do this, specify "uint" as the getter's type when calling Class_getValue. This guarantees that the return value will be the object's address regardless if the object is a wrapper or not.

For container objects, use ContainerClass_getAddress instead of ContainerClass_get to return the object's address (even for wrappers) - ContainerClass_get will unwrap wrapper objects.

Index value

The index specifies which value you wish to get or set. See types for details on the different types of values you can get or set.

By using the same index for your getter and setter, you are able to set and retrieve class values.



Each index has the following form: [b]WordIndex[a|b|c]-BitIndex


BitIndex: the bit index is for working with bit flags (see below).


Built-in values: specify "b" followed by the index
Ex: "b1", "b2a", "b3c-4"

User-defined values: same format, but omit the "b"
Ex: "1", "2a", "3c-4". You can also use 1 instead of "1" as AHK treats them the same.


WordIndex: the word index (starting at 1)

Since most types (String, SString, UInt, Int, Float, obj, robj, ptr, and sptr) are 4 bytes (1 word), most of the time you will be working in whole words.

Example indexes: "b1" (first built-in word), "b2" (second built-in word), etc, and 1, 2, etc. (for user-defined values).


Byte offset (a|b|c):

There may be times when you wish to store values that are less than 4 bytes in size (char, uchar, short, ushort).

You can specify the byte offset to "pack" multiple values in the same word (4 byte space).


Example1: store two shorts (2 bytes each) in the same word

By specifying "b1" (for the first short) and "b1b" (for the second), you would "pack" the two shorts in the first (built-in) word. The trailing "b" in the second index means "two bytes later". Since short is two bytes, "two bytes later" than "b1" is "b1b".


Example2: storing a char (1 byte), ushort (2 byte), and uchar (1 byte) in the same word

To pack these three values into the first (built-in) word, the first value would be stored in "b1" (type "char"). Since char is one byte, the next value would be "b1a" (type "ushort"). Then, two bytes later (since ushort is 2 bytes), "b1c", you would store the final value, the 1 byte uchar.

Bit flags

There may be times when you want to set a value to a boolean value (True or False). In order to save space, you can use bit flags to "pack" several of these booleans into the same space.

Another use for bit flags is for working with Bit masks.


Bit flags are not a different type - only an extension of the regular types.

The valid types for working with bit flags are Char/UChar, Short/UShort, Int/UInt.


The number of bits in the value (the size) is the same as the number of bit flags.

Char/UChar has 8 bits (1 byte), so there are 8 bit flags to use: 1 through 8.
Short/UShort has 16 bits (2 bytes), so there are 16 bit flags to use: 1 through 16.
Int/UInt has 32 bits (4 bytes), so there are 32 bit flags to use: 1 through 32.


To get or set a bit flag, specify the bit index (starting with 1) for the index value in the class getters and setters.

When getting a bit flag, the return will be 0 if unset and non-zero if set. The actual value is the bit mask for the bit flag.

When setting a bit flag, only the specified bit flag is modified - the others remain as they were. The "value" to set dictates the behavior.

Specify a non-zero value (or any non-empty string) to set the bit flag, 0 to clear the bit flag, and the empty string to only return the previous value (will not modify the bit flag).



There are two "modes" for setting bit flags: One that uses one function for both getting and setting, and the second which uses a separate function for each.


BitFlags -> One function getter/setter

When working with bit flags, Class_setValue is overloaded to allow using the set function as both a getter and setter.


Ex: Rectangle_isStriped

Rectangle_isStriped(RectangleObject, isStriped = "")
{
    ;word 3, byte 1 is a uchar type
    return Class_setValue(RectangleObject, "3-8", isStriped, "uchar")
}

In this mode, the function's second parameter has the empty string as it's default value. This allows the previous value to be returned if the second parameter is omitted.

To set the bit flag, specify a non-zero value (or a non-empty string) for the second parameter.

To clear the bit flag, pass 0 for the second parameter.


BitFlags -> Separate functions for getter & setter

In this mode, one function is used to get the bit flag and another to set it. This mimics the behavior for other getters and setters.


Ex: Rectangle_hasDots (getter) and Rectangle_setDots (setter)

Rectangle_hasDots(RectangleObject)
{
    ;word 3, byte 1 is a uchar type
    return Class_getValue(RectangleObject, "3-5", "uchar")
}

Rectangle_setDots(RectangleObject, hasDots)
{
    ;word 3, byte 1 is a uchar type
    return Class_setValue(RectangleObject, "3-5", hasDots, "uchar")
}

To get the bit flag, call Rectangle_hasDots(RectangleObject) - the getter function.

To set the bit flag, specify a non-zero value (or a non-empty string) for the second parameter.

To clear the bit flag, pass 0 for the second parameter.


Note: passing the empty string for "hasDots" WILL NOT clear the bit flag - since the library doesn't know if you are using the one-function or two-function mode.

However, this slight modification to the setter function allows both 0 AND the empty string to clear the bit flag; all other values will set the flag. (thanks bmcclure)

Rectangle_setDots(RectangleObject, hasDots)
{
    ;word 3, byte 1 is a uchar type
    hasDots := hasDots ? true : false
    return Class_setValue(RectangleObject, "3-5", hasDots, "uchar")
}

BitFlags -> Both bit flag modes

Regardless if you use the one-function mode or the two-function mode for bit flags, you can store / load the bit flags with the following functions.


These functions will store / load all bit flags for a specific value.

For example, both the "isStriped" and "hasDots" bit flags are in the third user-defined word ("3") and the type is ("uchar").


To store the bit flags in a variable:

Rectangle_getFlags(RectangleObject)
{
    ;word 3, byte 1 is a uchar type
    return Class_getValue(RectangleObject, 3, "uchar")
}

Then, to load them from a variable into an object:

Rectangle_setFlags(RectangleObject, flags)
{
    ;word 3, byte 1 is a uchar type
    return Class_setValue(RectangleObject, 3, flags, "uchar")
}

Bit masks

Write-once values

By using a bit flag, you can specify a value to be write-once.

A write-once value can be written one time, but no more. Note, you can retrieve the value as many times as you wish.


Ex: Rectangle_setColor

Rectangle_setColor(RectangleObject, Color)
{
    ;use 3-6 (which is empty) to store the "is already written?" bit-flag for the Rectangle's color
    return Class_CanWrite(RectangleObject, "3-6", "uchar")
        ? Class_setString(RectangleObject, 1, Color)
        : Class_getString(RectangleObject, 1)
}

Details:

Class_canWrite is used to test if the value can be written.

If the specified bit flag is set, then Class_CanWrite returns false (0), because the write-once value was already written to. Then, the function returns the previous String value (via Class_getString).

If the specified bit flag is unset, then it is first set (to prevent future sets, in accordance with the write-once behavior). Then Class_canWrite returns true (1) to state that the value can be written. Finally, the value is set and the previous string value is returned (via the return from Class_setString).


Since a bit flag is used to decide whether the value can be set, you can always clear the bit flag (like you can with any bit flag) to allow setting the value again.

Note: when creating a clone, since the write-once bit flag is treated like any other bit flag, it will be copied into the clone. This means, for example, if a Rectangle object (whose color was already set) was cloned, then the clone would not be able to modify it's color. You can change this behavior by unsetting the write-once flag (see Rectangle_clone_private for details).



Read-only values:

Read-only values function similarly to write-once values, but they don't have a set function - so there is no need to use a bit flag to test if the value can be set.

Instead, a read-only value is set when the object is created (and not changed afterwards - hence, read-only).

Like any other value, you still set the value using one of the Class setters. The only difference is that with a normal value, you wrap this call in a setter function. However, since the value is read-only, there is no setter function; therefore, you make the call to the Class setters directly.

Class Library V2

YourClass_getBuiltInValues

YourClass_getUserDefinedValues

Destroying objects

When you call YourClass_destroy(YourClassObject), you might expect the object to no longer exist after the call. This is only half-true.

Each class object has a lock count. The object will not be destroyed if its lock count is more than one (meaning the object exists in more than one location).

This allows storing the same object in multiple locations and having it remain valid until the last object that uses it is destroyed.

Indexes "wrap"

By "wrap" I mean the following: If you specify an index outside the "normal bound", 1 <= index <= size (the number of items), then the index will be changed to one within this bound.


Specifically:

If the index is greater than size, size is used for the index.
If the index is less than one, index + size is used.
    Ex. Zero is changed to size, -1 to size - 1, etc.
If even after this wrapping, the value is still not within the "normal bound" (i.e. still less than 1), 1 is used for the index.


Some notes:
  1. Regardless of the size, 0 can be used to mean "the last item" or "the end of the list".
  2. The returned index can only be outside the "normal bound" when size = 0; in this case, 0 is returned for the index.

Set vs Unset elements

A "set" value refers to an obj value (e.g. an object in a container class) which has a (non-null) object stored.


"Unset" refers to an element that has a NULL (0) stored instead of an object.

For Arrays, all entries are NULL (unset) when the Array is created.


For containers, elements can be set (or unset) by calling the class' setter functions.


Homepage