AMXXPawn operator关键字
operator关键字用于声明运算符重载语句,同时也是声明一个拥有假名的函数。
AMXXPawn的运算符重载本质是设定一个文本替换规则。
编译器会检查源码中的复合表达式,若运算符对得上,且所有操作数的值类型标签对得上,则替换为函数调用。
由于函数只能在插件运行时调用,所以满足替换条件的表达式不可能是常量表达式。
各类运算符重载语句,()是必填项目,【】是可选项目:
声明forward函数:
【forward】 【值类型标签】(函数假名)(参数列表)【;】
声明、定义native函数:
(native) 【值类型标签】(函数假名)(参数列表) (=) (真名可见的native函数)【;】
(native) 【值类型标签】(函数假名)(参数列表) (=) (单值常量表达式)【;】
声明、定义公共函数:
(public) 【值类型标签】(函数假名)(参数列表) (函数体)
声明、定义文件私有函数:
(static) 【值类型标签】(函数假名)(参数列表) (函数体)
声明、定义文件私有备用函数:
(static stock) 【值类型标签】(函数假名)(参数列表) (函数体)
(stock static) 【值类型标签】(函数假名)(参数列表) (函数体)
声明、定义插件私有函数:
【值类型标签】(函数假名)(参数列表) (函数体)
声明、定义插件私有备用函数:
(stock) 【值类型标签】(函数假名)(参数列表) (函数体)
(函数假名)语法拆解:
(operator)(运算符)
运算符重载语句和函数声明语句基本上完全相同,除了native函数声明语句,以及函数名称的设定。
关于“函数假名”:
- “函数假名”用于定义要匹配的运算符,可匹配的运算符有:+ - * / % ++ -- == != < > <= >= ! =
- 函数真名在编译时自动生成,因此通常无法得知。如果需要让外部插件调用,可定义一个接口函数进行跳转
关于“参数列表”:
- “参数列表”实际上是运算符的操作数列表
- 需要1个参数的运算符:++ -- ! =
- 需要2个参数的运算符:+ - * / % == != < > <= >=
- 参数不能传引用
关于“参数的值类型标签”:
- 复合表达式的操作数与参数的标签相同时,就会进一步满足替换条件
- 编译器优化可能会交换左右两个操作数位置,若两个操作数标签不同,最好同时设定_: + Float:和Float: + _:两种匹配模式
- 需要2个参数的运算符:+ - * / % == != < > <= >=
关于“函数的值类型标签”:
- “函数的值类型标签”也就是返回值标签,运算符计算结果的标签
- !运算符的返回值必须带有bool标签
- 其它运算符的返回值可以自定义标签
- =的返回值标签同样是替换条件之一,因此,只有它可以有多个拥有相同“参数列表”的重载
其它:
- 示例代码可以参照float.inc文件,该文件定义了拥有Float标签的操作数该如何运算,重载了几乎所有可重载的运算符
- ++ --不区分前置或后置,在文本替换时会自动处理,用户只需定义计算方式
- 变量声明语句会隐式调用标签:v=标签:0运算,自动完成变量定义
关于bug:
AMX Mod X 1.8.2 至 1.10.0版本,++和--运算会指向同一函数地址。
这会导致--运算实际触发的是++运算。
例子:
stock Float:operator++(Float:oper)
return oper+1.0;
stock Float:operator--(Float:oper)
return oper-1.0;
public plugin_precache()
{
new Float:f = 5.0;
f--;
server_print("f = %f", f);
}
修复方法:
找到编译器源码sc3.c文件中,以下代码:
static void user_inc(void) { }
static void user_dec(void) { }
用下面的代码覆盖再重新编译,得到新的amxxpc32.dll,替换旧文件。
static void user_inc(void) { }
static void user_dec(void) { __asm { nop }; }
逃避方法:
将插件源码中f--或--f之类的代码改为f -= 1.0、f = f - 1.0、 f = floatsub(f, 1.0)等样式。
注意事项:
v--或v++表达式的返回值是变量v的地址,而不是v的值。
建议:
由于运算符重载本质是将复合表达式替换为函数调用,直接调用函数实际上更可靠,可避开各种未知bug。