函数指针(function)
function类型作为一个函数指针,类似于C++里面的(void*) func,它可以作为一个函数的对象,通过function进行传递。 我们可以通过(: function_name, function_args... :)的形式可以定义一个函数指针 并可以以(*ptr)(...)的形式进行调用。
例如:
function f = (: printf, "%s" :); // 使用 '(:' 和 ':)' 来定义,带参数
(*f)("abc"); // 输出 "abc"
function func = (: sizeof :) // 使用 '(:' 和 ':)' 来定义,不带参数
int size = func.call_local("hello world") // size = 11
当然我们可以定义自己的函数,举个例子:
//计算两个数值的最大值
public int maxer(int x, int y)
{
return x > y ? x : y;
}
void test()
{
int x = 102;
int y = 32;
function fun = (: maxer :);
int ans = fun.call_local(x, y);
write(ans);
}
test();
上面函数执行完之后会输出x和y中的最大值,也就是102
函数指针的参数域(arg domain)
顾名思义,函数指针的参数域是指函数指针拥有的参数变量所在的域;
-
函数指针不带有参数变量时,参数域取决于函数原型是不是parallel
-
原型为parallel的,参数域为nil
-
原型为非parallel的,参数域为函数指针的owner(ob instance)所在的域
- 可以使用function.info().object查看函数指针的owner
f1.gs//
void create()
{
}
public void say()
{
}
public parallel void yell()
{
}test.gs#pragma parallel
import .f1;
void create()
{
}
public void test()
{
object ob = new_object(f1, domain.create());
// 对象的域
printf("ob domain: %O\n", ob.get_domain());
// 定义一个不带参数变量的函数指针: 函数原型为parallel
function f1 = (: ob.yell :);
printf("f1 arg domain: %O\n", f1.get_arg_domain());
// 定义一个不带参数变量的函数指针: 函数原型为非parallel
function f2 = (: ob.say :);
printf("f2 arg domain: %O\n", f2.get_arg_domain());
printf("f2 ob instance: %O\n", f2.info().object);
printf("f2 ob instance domain: %O\n", f2.info().object.get_domain());
}import test;
test.test();输出结果在此:
ob domain: domain[13:v1]d3
f1 arg domain: nil
f2 arg domain: domain[13:v1]d3
f2 ob instance: object[34:v2]/f1.gs
f2 ob instance domain: domain[13:v1]d3
-
-
函数指针带有参数变量时,参数域取决于参数列表中的参数类型
-
参数列表中拥有非read only的buffer/array/map/function参数时,参数域就是该参数所在的域
f1.gs//
void create()
{
}
public void say()
{
}
public parallel void yell(...)
{
}test.gs//
#pragma parallel
import .f1;
void create()
{
}
public void test()
{
object ob = new_object(f1, domain.create());
// 参数域中不带有非read only的buffer/array/map/function参数
int a1 = 1;
string a2 = "2";
map a3 = {};
array a4 = [];
printf("this domain: %O\n", this_domain());
// 带了buffer/array/map/function参数
// 参数域就是参数所在的域(也就是当前域 this_domain())
function f = (: ob.yell, a1, a2, a3, a4 :);
printf("f arg domain: %O\n", f.get_arg_domain());
}import test;
test.test();this domain: domain[0:v1]zero
f arg domain: domain[0:v1]zero -
参数列表中不拥有非read only的buffer/array/map/function参数时,参数域和该函数指针不带有参数变量时是一样的
f1.gs//
void create()
{
}
public void say()
{
}
public parallel void yell(...)
{
}test.gs
#pragma parallel
import .f1;
void create()
{
}
public void test()
{
object ob = new_object(f1, domain.create());
// 参数域中不带有非read only的buffer/array/map/function参数
int a1 = 1;
string a2 = "2";
map a3 = make_readonly({});
array a4 = make_readonly([]);
// 这里:通过类型转换获得的buffer是readonly的
buffer a5 = (buffer) "abc";
// 带了一堆非read only的buffer/array/map/function参数
// 参数域和不带参数相同
function f = (: ob.yell, a1, a2, a3, a4, a5 :);
printf("f arg domain: %O\n", f.get_arg_domain());
function nf = (: ob.yell :);
printf("nf arg domain: %O\n", nf.get_arg_domain());
}import test;
test.test();输出在此:
f arg domain: nil
nf arg domain: nil
-
改变函数指针的参数域
-
未绑定参数域的函数指针可以通过一些方法来绑定参数域
-
function.bind_arg_domain: 直接将函数指针绑定到指定的参数域中
test.gs#pragma parallel
void create()
{
}
public void test()
{
// 此时没有绑定参数域
function f = () {};
printf("f arg domain 1: %O\n", f.get_arg_domain());
// 绑定到指定的参数域上
domain d = domain.create();
f.bind_arg_domain(d);
printf("f arg domain 2: %O\n", f.get_arg_domain());
}import test;
test.test();输出在此:
f arg domain 1: nil
f arg domain 2: domain[11:v1]d1 -
function.append_arg_arr/function.append_arg:通过增加函数指针的非read only的buffer/array/map/function参数绑定参数域
test.gs
#pragma parallel
void create()
{
}
public void test()
{
// 此时没有绑定参数域
function f = () {};
printf("f arg domain 1: %O\n", f.get_arg_domain());
// 为函数指针增加一个buffer/array/map/function类型参数绑定参数域
array a1 = [];
f.append_arg(a1);
printf("f arg domain 2: %O\n", f.get_arg_domain());
// 此时没有绑定参数域
function f2 = () {};
printf("f2 arg domain 1: %O\n", f2.get_arg_domain());
// 为函数指针增加一组buffer/array/map/function类型参数绑定参数域
array arr = [ {}, [] ];
f2.append_arg_arr(arr);
printf("f2 arg domain 2: %O\n", f2.get_arg_domain());
}import test;
test.test();输出在此:
f arg domain 1: nil
f arg domain 2: domain[0:v1]zero
f2 arg domain 1: nil
f2 arg domain 2: domain[0:v1]zero
-
-
已经绑定参数域的函数指针无法改变参数域
-
特别的,不带有参数的已经绑定参数域的函数指针可以通过function.remove_ob_instance()移除函数指针的owner(ob instance),从而解绑定参数域
test.gs
public void test()
{
// 此时已绑定参数域
function f = () {};
printf("f arg domain 1: %O\n", f.get_arg_domain());
// 通过function.remove_ob_instance()移除owner,从而解除绑定参数域
f.remove_ob_instance();
printf("f arg domain 2: %O\n", f.get_arg_domain());
}import test;
test.test();输出在此:
f arg domain 1: domain[0:v1]zero
f arg domain 2: nil
-
函数指针的几种调用方式
-
call_local: 在当前域下调用函数指针
- 函数的参数域为nil或者参数域于当前域相同时,才能调用函数指针
- 函数的参数域非nil时并且和当前域不同时,调用会出错
test.gs
#pragma parallel
import .f1;
public void test()
{
function fn = (int i) {
printf("hello: %d\n", i);
};
// 参数域为nil,可以被调用
fn.call_local(1);
// 函数绑定参数域为当前域
fn.bind_arg_domain(this_domain());
// 调用域和函数的参数域一致,可以被调用
fn.call_local(2);
function fn2 = (int i) {
printf("hello: %d\n", i);
};
// 函数绑定参数域为非当前域
fn2.bind_arg_domain(domain.create());
// 调用域和函数的参数域不一致,调用报错
fn2.call_local(2);
}import test;
test.test();输出在此:
hello: 1
hello: 2
Error(-1): Can not call function local since not in expected domain, current domain is 'domain[0:v1]zero' expected 'domain[18:v1]d8'
Coroutine: co[12:v1]shell
Domain: domain[0:v1]zero [local call]
At unknown:0 (call_local) in object[31:v2]
Argument f = N/A
Argument $2 = 2
Domain: domain[0:v1]zero [local call]
At /test.gs:33 (test) in object[31:v2]
Variable fn = (: .#closure_0_in_test :)object[31:v2]/test.gs<Parallel>
Variable fn2 = (: .#closure_1_in_test :)object[31:v2]/test.gs<Parallel>
Domain: domain[0:v1]zero [local call]
At /@shell_10.gs:8 (::entry) in object[34:v4]
Domain: domain[0:v1]zero [local call]
Domain: domain[0:v1]zero [local call]
Domain: domain[0:v1]zero [local call]
Domain: domain[0:v1]zero [local call]
At <Internal routine> -
call: 在函数指针的参数域中调用该函数指针
- 如果函数指针的参数域为nil,则在当前调用域中调用该函数指针
- 如果函数指针的参数域不为nil,则自动切域至函数指针的参数域后再调用该函数指针
- 如果函数指针的参 数域和当前调用域相同时,不需要切域
- 如果函数指针的参数域和当前调用域不同时,则会自动切域
f1.gsvoid create()
{
}
public void say()
{
printf("this domain@say: %M\n", this_domain());
}
public parallel void yell()
{
printf("this domain@yell: %M\n", this_domain());
}test.gs
#pragma parallel
import .f1;
public void test()
{
// 当前调用域
printf("this domain: %M\n", this_domain());
object ob = new_object(f1, domain.create());
printf("---1\n");
// fn的参数域不为空
function fn = (: ob.say :);
// 切域至fn的参数域后调用函数指针
fn.call();
printf("---2\n");
// fn2的参数域为空
function fn2 = (: ob.yell :);
// 直接在当前调用域里调用函数指针
fn2.call();
}import test;
test.test();输出在此:
this domain: domain[0:v1]zero
---1
this domain@say: domain[11:v1]d1
---2
this domain@yell: domain[0:v1]zero -
call_by_instance: 为函数指针临时指定owner后再进行call调用
- 函数指针执行call_by_instance调用时,需要保证函数指针的参数域为nil或者和新owner的域相同,否则调用错误
- function.remove_ob_instance的作用是移除函数指针的owner
- 参数域不为nil的函数指针,不拥有非read only的buffer/array/map/function的参数时,可以通过function.remove_ob_instance移除owner从而设置参数域为nil
f1.gs
void create()
{
}
public void say(mixed v)
{
printf("this domain@say: %M\n", this_domain());
printf("this object@say: %M\n", this);
}
public parallel void yell(mixed v)
{
printf("this domain@yell: %M\n", this_domain());
printf("this domain@yell: %M\n", this);
}test.gs
#pragma parallel
import .f1;
public void test()
{
object ob = new_object(f1, domain.create());
defer ob.close();
object ob2 = new_object(f1, domain.create());
defer ob2.close();
// 有参数域,参数中没有非read only的buffer/array/map/function
function f1 = (: ob.say, 1 :);
// 有参数域,参数带有非read only的buffer/array/map/function
function f2 = (: ob.say :);
f2.append_dup_arg([]);
// 无参数域: 参数中没有非read only的buffer/array/map/function
function f3 = (: ob.yell, 1 :);
// 有参数域,参数带有非read only的buffer/array/map/function
function f4 = (: ob.yell :);
f4.append_dup_arg([]);
debug.enable_exception_log(ErrorCode.UNKNOWN_ERROR, false);
defer debug.enable_exception_log(ErrorCode.UNKNOWN_ERROR, true);
// f1参数域可以通过remove_ob_instance解除
printf("CALL f1:\n");
f1.remove_ob_instance();
f1.call_by_instance(ob2);
// f2参数域无法通过remove_ob_instance解除
printf("CALL f2:\n");
try {
f2.remove_ob_instance();
// 这个会报错
f2.call_by_instance(ob2);
}
catch {
printf("f2 error occur.\n");
};
// f3无参数域,不需要remove_ob_instance
printf("CALL f3:\n");
f3.call_by_instance(ob2);
// f4有参数域,无法通过remove_ob_instance解除参数域,调用报错
printf("CALL f4:\n");
try {
f4.remove_ob_instance();
f4.call_by_instance(ob2);
}
catch {
printf("f4 error occur\n");
};
}import test;
test.test();输出在此:
CALL f1:
this domain@say: domain[29:v1]d19
this object@say: object[45:v2]/f1.gs
CALL f2:
Error(-1) ignored.
f2 error occur.
CALL f3:
this domain@yell: domain[0:v1]zero
this domain@yell: object[45:v2]/f1.gs
CALL f4:
Error(-1) ignored.
f4 error occur
闭包的一些说明(closure)
-
闭包的概念和c++应该是一致的(等名词解释页补充吧);简单理解就是一个匿名函数指针;
- 闭包和普通函数的最大的区别是: 闭包可以捕获闭包外的调用栈上的变量
test.gs#pragma parallel
public void test()
{
int n = 1;
// 定义一个闭包
function fn = () {
// 捕获闭包外的的调用栈上的变量
printf("n=%d\n", n);
};
// 调用
fn.call();
}import test;
test.test();