Code Migration - graphitemaster/gmqcc GitHub Wiki
Migration to GMQCC specific code and features
As you may have guessed, gmqcc shouldn't "just" replace your compiler, but it should also add some features will make your life easier, and some which are simply overdue. Of course the current focus is still on being compatible, but nonetheless we're slowly planning and adding features to come.
UTF-8 Support
QCFLAGS: -futf8
Description
For a while now, darkplaces supports utf-8 strings. Using an utf-8 string in QC code always worked. But there are 2 additions with this flag:
-
utf-8 character constants.
float chr = '∑';
will correctly setchr = 0x2211
. -
utf-8 escape sequences in strings and character constants. In the code
string foo = "\{x2211}";
the U+2211 constant will be utf-8 encoded.
Migration
Nothing special required.
The ternary operator
QCFLAGS: -fcorrect-ternary
Description
This changes the ternary's precedence to behave like in C.
So you can safely write print(a ? str1 : str2, "\n");
Note however that by default a warning will be displayed for this kind of ternary use.
Migration
In order to make sure your code does not suddenly start behaving differently, enable the ternary-precedence warning with -Wternary-precedence
, then fix all warnings. After that you can switch to using -fcorrect-ternary
.
Enhanced switch statements
TODO
varargs support
QCFLAGS: -fvariadic-args
Description
With this option it is possible to access varargs from within QC. This is done with the following syntax:
...(parameter, TYPE)
The parameter
can be an expression, or a constant. Note that much like with arrays, constants
will cause direct access, while variable expressions will call an accessor function. The TYPE
can
be any type without attributes.
In order to know how many parameters were passed to a function, the ...
parameter has to be
suffixed with a name, which will implicitly become a local variable of type float
containing the
number of varargs.
Here's an example:
void print_nth_string(float n, ...count) {
if (n >= count)
print("*** invalid parameter\n");
else
print("String ", ftos(n), " is: `", ...(n, string), "`\n");
}
Additionally, a type-restriction syntax has been added, so you can make sure the function
cannot be called with types other than string by changing ...count
into string...count
:
void print_nth_string(float n, string...count) {
if (n >= count)
print("*** invalid parameter\n");
else
print("String ", ftos(n), " is: `", ...(n, string), "`\n");
}
Migration
None required.
Type restricted variadic arguments
Description
In order to be a bit stricter concering parameter types of variadic functions, we added a way to
restrict the type of all variadic parameters at once. This is done by prefixing the ...
with a type.
For instance, to turn the print
builtin into a function which accepts a variable amount of strings:
void print(string...) = #1234;
Migration
Not required, but we suggest changing builtins like print(...)
into print(string...)
.
Labeled loops
QCFLAGS: -floop-labels
Description
Labeling a loop allows you to use break
and continue
from nested loops to break out of outer loops.
Just look at this example code:
void print(...) = #1;
string ftos (float) = #2;
void main(float a, float b) {
float i, j;
for :outer (i = 0; i < 5; ++i) {
for (j = 0; j < 8; ++j) {
print(ftos(j), " ");
if (i == a && j == b) {
print("OUT\n");
continue outer; // or 'break' to stop both loops
}
}
print("\n");
}
}
Obviously this is different from simply putting a label in front of a loop and using goto
in that
a continue
will for instance jump to a for
-loop's incrementing-statement, while a break
will jump out of the loop. Using regular labels with goto
would require multiple labels for this type of behaviour.
Migration
Nothing special required. There's no option to help you find goto
using code which could be improved with loop-labels. You can however just grep your source for goto
, it shouldn't yield many results. If it does, change that, because goto
yields bad code (and disables some optimizations).
Untyped null-constant
QCFLAGS: -funtyped-nil
Description
Often you see QC code declare various kinds of null
variables which are simply uninitialized globals. Like a void SUB_null();
. Usually one such function suffices, but modern compilers will warn when trying to assign this function to one of a different signature.
For this we added an untyped nil
, which can be assigned to anything: functions, entities, strings, floats.
void clear() {
self.other = nil;
self.think = nil;
self.name = nil; // NOT the same as assigning ""
}
Migration
There are 2 warn-flags which are useful to find globals like SUB_null
: -Wuninitialized-constant
and -Wuninitialized-global
. The first will warn about uninitialized globals declared using the const
keyword, the other one will warn about any global without the var
keyword which is not initialized.
AST Macros
TODO