如何为C函数创建闭包

时间:2015-03-18 08:08:34

标签: c lambda closures common-lisp ecl

我正在使用C API与ECL交互,我试图从具有一些存储状态的本机函数创建一个闭包对象。

我试过这个:

cl_object f(long nargs, ...) {
    std::cout << nargs << std::endl;

    std::cout << "has value?" << std::endl;
    cl_print(1, cl_boundp(c_string_to_object("HI")));
    std::cout << "\ndone" << std::endl;

    return Cnil;
}

auto closure = ecl_make_cclosure_va(f, c_string_to_object("((HI . 2))"), Cnil);
ecl_defparameter(c_string_to_object("F"), closure);

这使得函数f可以在lisp中使用(funcall f)进行调用,但cl_boundp始终在nil的正文中返回f。如何构造env(环境)参数到ecl_make_cclosure_va,以便本机代码可以读取闭包中的值?我认为这只是一个alist,但显然不是,我没有找到在Google上构建一个的例子。

1 个答案:

答案 0 :(得分:1)

编辑:我先添加了第二个,相当干净,简短的答案,这可能并不是你想要的,但可能只是足够接近。原来的,更长的答案说明为什么要做你想要的事情很复杂,但建议可能的起点仍然存在。

更容易接近

我已经根据ECL界面概述了这一点,但它可能主要通过FFI接口完成,以实现更清晰,更便携的方法。

  1. 定义一个c函数,它接受所需的所有变量,包括闭包变量
  2. 代码:

    // 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
    }
    
    1. 包装函数以便它可以通过lisp(ecl_def_c_function?)

    2. 调用
    3. 用lisp包装封口

    4. 代码:

      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);
      
      1. 调用包装的C函数。
      2. 原始回答

        这是一个稍长的回答,说&#34;不容易&#34;但是:

        了解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,并使其附加xy列表。然后它调用ecl_make_cclosure_va。在LC1_g0(lambda的包装)中,它使用cl_object env0 = cl_env_copy->function->cclosure.env;访问环境。

        请注意,访问环境中的值是因为它知道顺序,而不是按名称获取它们。

        你基本上必须复制这种机制来访问ac函数中的闭包变量(尽管你可以生成一个以模式方便的方式存储值的环境(例如(HI 2)的列表)。 / p>

        您最好的选择可能是使用ecl_bds_bind将环境中的位绑定为特殊变量(请参阅http://ecls.sourceforge.net/new-manual/re04.html)。如果你这样做,那么我想你可以使用HI。虽然要注意它是动态的而不是词法上的束缚。

        虽然做了很多工作!这真的不是一个面向用户的界面&#34;。