編集:最初に2番目のかなりクリーンで短い回答を追加しました。やりたいことを正確に行うのが複雑な理由を説明する元の長い回答ですが、その後に可能な出発点が残っていることを示唆しています。
より簡単なアプローチ
これは ECL インターフェースの観点から概説してきましたが、よりクリーンで移植性の高いアプローチのために、FFI インターフェースを使用して大部分を行うことができます。
- クロージャー変数を含む、必要なすべての変数を取る ac 関数を定義します
コード:
// defined here as a non-varargs function - you'll need to change it slightly
cl_object f(cl_object hi, cl_object func_param1, cl_object_fund_param2) {
// note you can pass hi back to lisp fairly easily
cl_object name = ecl_make_symbol("do-something-useful","CL-USER");
cl_funcall(2,name,hi);
// some other stuff
}
Lisp から呼び出せるように関数をラップします ( ecl_def_c_function
?)
クロージャを Lisp でラップする
コード:
cl_object wrapped_c_function = cl_safe_eval(c_string_to_object(
"(let ((hi 2))
#'(lambda (x y) (your-c-function hi x y)))"),Cnil,Cnil);
- ラップされた C 関数を呼び出します。
元の答え:
これは「簡単ではない」と言うには少し長い答えですが、
ecl の機能を理解する最も簡単な方法は、ecl を使用して簡単なスクリプトを C にコンパイルすることです ( ecl -c <filename.c> -compile <filename.lisp>
)。これは、可変引数リストを使用してクロージャを生成する簡単な Lisp です。
(defun make-closure-function (x y)
#'(lambda (&rest arguments) (apply '+ (append (list x y) arguments))))
(defun main ()
(let ((f (make-closure-function 1 2)))
(print (funcall f 3)))
(format t "~%"))
(main)
そして、これが生成する C コードの関連部分です。
/* function definition for MAKE-CLOSURE-FUNCTION */
/* optimize speed 3, debug 0, space 0, safety 2 */
static cl_object L2make_closure_function(cl_object v1x, cl_object v2y)
{
cl_object env0;
cl_object CLV0, CLV1;
const cl_env_ptr cl_env_copy = ecl_process_env();
cl_object value0;
ecl_cs_check(cl_env_copy,value0);
{
env0 = ECL_NIL;
CLV0 = env0 = CONS(v1x,env0); /* X */
CLV1 = env0 = CONS(v2y,env0); /* Y */
{
cl_object v3;
v3 = ecl_make_cclosure_va((cl_objectfn)LC1__g0,env0,Cblock);
value0 = v3;
cl_env_copy->nvalues = 1;
return value0;
}
}
}
/* closure G0 */
/* optimize speed 3, debug 0, space 0, safety 2 */
static cl_object LC1__g0(cl_narg narg, ...)
{
cl_object T0, T1;
cl_object CLV0, CLV1;
const cl_env_ptr cl_env_copy = ecl_process_env();
cl_object env0 = cl_env_copy->function->cclosure.env;
cl_object value0;
ecl_cs_check(cl_env_copy,value0);
/* Scanning closure data ... */
CLV1 = env0; /* Y */
CLV0 = _ecl_cdr(CLV1);
{ /* ... closure scanning finished */
{
cl_object v1arguments;
ecl_va_list args; ecl_va_start(args,narg,narg,0);
v1arguments = cl_grab_rest_args(args);
ecl_va_end(args);
T0 = cl_list(2, ECL_CONS_CAR(CLV0), ECL_CONS_CAR(CLV1));
T1 = ecl_append(T0,v1arguments);
value0 = cl_apply(2, ECL_SYM("+",14), T1);
return value0;
}
}
}
/* function definition for MAIN */
/* optimize speed 3, debug 0, space 0, safety 2 */
static cl_object L3main()
{
cl_object T0;
const cl_env_ptr cl_env_copy = ecl_process_env();
cl_object value0;
ecl_cs_check(cl_env_copy,value0);
{
TTL:
{
cl_object v1f;
v1f = L2make_closure_function(ecl_make_fixnum(1), ecl_make_fixnum(2));
T0 = ecl_function_dispatch(cl_env_copy,v1f)(1, ecl_make_fixnum(3));
ecl_print(T0,ECL_NIL);
}
value0 = cl_format(2, ECL_T, VV[1]);
return value0;
}
}
で作成env0
し、を付けmake_closure_function
てリストにします。次に、 を呼び出します。(ラムダのラッピング) では、 で環境にアクセスします。x
y
ecl_make_cclosure_va
LC1_g0
cl_object env0 = cl_env_copy->function->cclosure.env;
環境内の値は、名前で取得できるのではなく、順序を知っているためにアクセスされることに注意してください。
ac 関数のクロージャー変数にアクセスするには、基本的にこのメカニズムを複製する必要があります (ただし、モードに便利な方法で値を格納する環境を生成できます (たとえば、のリスト(HI 2)
)。
ecl_bds_bind ( http://ecls.sourceforge.net/new-manual/re04.htmlを参照) を使用して、環境からのビットを特殊変数としてバインドするのが最善の方法でしょう。あなたがそれをしたら、私はあなたが使うことができると思いますHI
。ただし、語彙的にバインドされるのではなく、動的にバインドされることに注意してください。
大変な作業ですが!これは実際には「ユーザー向けインターフェース」ではありません。