Defining classes
C modules may define classes and their members using a variety of macros described in this section and the following sections.
All named members are public by default. Section Private definitions describes how to define private members.
Basics
A class definition starts with the macro A_CLASS (or one of its variants), and ends with A_END_CLASS(). The structure of the class, including its members and inheritance relationships, is defined within these macros using the macros described later in this section and in the following sections.
- A_CLASS(name)
- Define a C class with the given name. The definition of the class follows this macro, and is terminated with the A_END_CLASS macro. Class definitions cannot be nested.
- A_CLASS_PRIV(name, numPrivate)
- This behaves like the A_CLASS macro described above, but also define numPrivate unnamed slots for private variables. These slots can be accessed only by using AMemberDirect and ASetMemberDirect.
- A_END_CLASS()
- Mark the end of a class definition.
Implementing methods
The type signature of the C function that implements a method in a class is the same as in a C function that implements and ordinary function, i.e. AValue <func>(AThread *, AValue *). The structure of the frame is also similar, but there is an additional self argument at index 0, and all other values in the frame are displaced by one item:
frame[0]: self frame[1]: Arg 1 (first argument, may be ADefault if optional and not provided by the caller) frame[2]: Arg 2 (second argument, may be ADefault if optional and not provided by the caller) ... frame[n]: Arg n (last argument, may be ADefault if optional not provided by the caller) frame[n+1]: VarArgs (optionally an Array containing the rest of the arguments) frame[n+2]: Temp 1 (first temporary location) ... frame[n+m+1]: Temp m (last temporary location)
There are more details available in section Implementing C functions that deals with global functions, but remember that methods have the additional implicit self argument.
Defining methods
Method definition macros are mostly identical to function definition macros, but the frames of the C functions will have the self argument as an additional item at the start of the frame.
- A_METHOD(name, numArgs, numTemps, cFunc)
- Define a method with a fixed number of arguments. This macro is used like A_DEF, but an additional self argument is stored at the start of the frame as described above. The self argument is not included in the number of arguments, i.e. numArgs only refers to visible arguments.
- A_METHOD_OPT(name, minArgs, maxArgs, numTemps, cFunc)
- Define a method with optional arguments. This is similar to A_DEF_OPT, except for the implicit self argument.
- A_METHOD_VARARG(name, minArgs, maxOrdinary, numTemps, cFunc)
- Define a method with an arbitrary number of arguments. This is similar to A_DEF_VARARG, except for the implicit self argument.
The create method
You can define a create method that is called during object construction in a C class, but unlike a create method implemented in Alore (that does not return a value), a C create method must always return self, i.e. frame[0], unless an uncaught exception was raised.
The create method must be used to initialize all member variables and constants defined in a C class since member initialization expressions cannot be defined using the C API. Inherited member initializers from an Alore superclass are executed normally, though.
Defining member variables
The macros A_VAR and A_EMPTY_CONST can be used within classes to define member variables and constants. Each member variable and constant is associated with a single (member variable) slot.
- A_VAR(name)
- Define a member variable with the specified name. The member variable will be initialized to nil during object construction, before calling create.
- A_EMPTY_CONST(name)
- Define a constant initialized to nil with the specified name. You should initialize the constant in the create method using ASetMemberDirect. You must not change the value of a member constant after leaving the constructor.
Defining accessors
Accessors (getters and setters) can be defined for C classes using the A_GETTER and A_SETTER macros. The ordinary rules for defining accessors apply, including these:
- You cannot define an accessor for a member that has been defined as a member variable or constant in the same class.
- You cannot define a setter unless there is also a getter defined for the same member.
- A_GETTER(name, numTemps, cFunc)
- Define a getter method for the member with the specified name. The C function should be implemented like a method that receives no arguments except self.
- A_SETTER(name, numTemps, cFunc)
- Define a setter method for the member with the specified name. The C function should be implemented like a method that receives a single argument (the assigned value) in addition to the self argument. A setter must always return ANil.
Inheritance
C classes inherit from std::Object by default, but they can optionally inherit from another class. The superclass can be any non-primitive type, implemented in C or Alore. Subclasses implemented in C can override methods and accessors. They can also access the original definitions in the superclass by using C API functions ASuperMember and ASetSuperMember.
- A_INHERIT(superName)
-
Make the current class inherit from another class. The name should the
fully qualified name of the superclass as a string. Use the
form ::A if the
superclass is defined in the current module. If the superclass
is defined in another module than the current module or std, that
module must also be imported in the current module using A_IMPORT.
You cannot define more than a single superclass for a class using
A_INHERIT.
Note: The A_INHERIT macro, if present, must always be the first A_ macro after the A_CLASS* macro that begins the class definition.
Interface implementation
C classes may implement interfaces using the A_IMPLEMENT macro. Any members defined in the interface must be implemented in the class or a superclass.
- A_IMPLEMENT(interfaceName)
-
Make the current class implement an interface. The name should be the
fully qualified name of the interface as a string. Use the form ::A if
the interface is defined in the current module. If the interface
is defined in another module than the current module or std, that
module must also be imported in the current module using A_IMPORT.
You can use multiple A_IMPLEMENT macros in a class to implement more
than one interface.
Note: The A_IMPLEMENT macros must always come immediately after the A_INHERIT macro (if present) or the A_CLASS* macro (otherwise).
Accessing member variable slots directly
The most efficient way of accessing member variables or constants is by accessing the member variable slots directly using a slot index. The first member variable or constant receives the slot index 0, the next 1, etc. Member variables defined in the superclasses always receive the smallest slot indices. Direct access can typically used to access only private members variables, since any member which might have an overridden accessor has to be accessed using other means.
Member slots can also be allocated implicitly for internal purposes. The macro A_EXTERNAL_DATA allocates an additional slot just like an A_VAR macro. If the #f method is defined in a class, a new slot is allocated, and the slot 0 is reserved for internal use. These slots are used internally by the Alore implementation and your code must never access them.