使用 Erlang 的 NIF,我应该如何在 ERL_NIF_TERM 上使用 malloc?

Using Erlang's NIF, How should I use malloc on ERL_NIF_TERM?

我正在使用 Erlang 的 NIF,C 函数的结果是一个数组,我想以三点元组列表的形式发回给 erlang,每个点都是两个双精度元组。

创建这个数组我正在这样做:

ans = (ERL_NIF_TERM *)malloc(6*ntri*sizeof(ERL_NIF_TERM));

for (i=0;i<ntri;i++) {
    ans[i] = enif_make_tuple3(env,
            enif_make_tuple2(env,enif_make_double(env,x1[i]),enif_make_double(env,y1[i])),
            enif_make_tuple2(env,enif_make_double(env,x2[i]),enif_make_double(env,y2[i])),
            enif_make_tuple2(env,enif_make_double(env,x3[i]),enif_make_double(env,y3[i]))
    );
}

到目前为止,它似乎有效。但这是正确的吗? 我的推理是,在数组 ans 的每个单元格上,我有 6 个双打,每个大小为 ERL_NIF_TERM,所以我根据这个进行分配。

但这是真的吗?

我应该数元组吗?

ERL_NIF_TERM 的大小是多少? ERL_NIF_TERM 中的 double 是否与 ERL_NIF_TERM 中的 int 大小相同?一个 2 个整数的元组也是一个 ERL_NIF_TERM,它的大小相同吗?

不,您的双精度值和 2,3 元组分配在堆上。它由 env 变量访问。因此,您只需为存储的 ntri ERL_NIF_TERM 分配 space。它们是您 enif_make_tuple3 调用的结果。您还应该尽可能使用 enif_alloc 而不是 malloc 进行分配(在您自己的代码中)。所以你的代码应该是这样的:

ERL_NIF_TERM *ans = enif_alloc(ntri*sizeof(ERL_NIF_TERM));
if(!ans) return enif_raise_exception(env, enif_make_atom(env, "insufficient_memory"));
// use enif_make_badarg(env) prior to R18

for (i=0;i<ntri;i++) {
    ans[i] = enif_make_tuple3(env,
            enif_make_tuple2(env,enif_make_double(env,x1[i]),enif_make_double(env,y1[i])),
            enif_make_tuple2(env,enif_make_double(env,x2[i]),enif_make_double(env,y2[i])),
            enif_make_tuple2(env,enif_make_double(env,x3[i]),enif_make_double(env,y3[i]))
    );
}

ERL_NIF_TERM result = enif_make_list_from_array(env, ans, ntri);
enif_free(ans);
return result;

现在关于第二个问题,ERL_NIF_TERM 的大小到底是多少? ERL_NIF_TERM 的大小是您平台上的一个词。它在 64b 上是 8B,在 32b 或 64b 半字上是 4B。它可以存储在堆栈或寄存器中,因此您不必分配所需的内存,您可以将它作为一个简单的参数传递给函数。

编辑: 你根本不用分配内存。直接构建结果列表效率更高

ERL_NIF_TERM result = enif_make_list(env, 0);

for (i = ntri; i;) {
    i--;
    result = enif_make_list_cell(env,
        enif_make_tuple3(env,
            enif_make_tuple2(env,enif_make_double(env,x1[i]),enif_make_double(env,y1[i])),
            enif_make_tuple2(env,enif_make_double(env,x2[i]),enif_make_double(env,y2[i])),
            enif_make_tuple2(env,enif_make_double(env,x3[i]),enif_make_double(env,y3[i]))
        ),
        result
    );
}

return result;