chicken-users
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Chicken-users] MV in foreign-lambdas


From: tonyg
Subject: Re: [Chicken-users] MV in foreign-lambdas
Date: Mon, 7 Oct 2002 23:12:29 +0100
User-agent: Mutt/1.2.5i

> OUT is a bit wierder since you can only have one of them in both C
> and Scheme--except with multiple values in scheme.

...and pass-by-reference in C, depending on the contract of the
individual function...

> Frankly, I've never really understood why multiple values is ever
> needed

Me too. You can always use lists. I'd be happier with R5RS
multiple-values if something like tuples was supported so that
multiple-values and multiple-arguments ended up being *totally*
symmetrical...

> (define foo (foreign-lambda* ((int z) (int x) (int y))
>                               "C_word lst = C_ALLOC(C_LIST_ELEMS(3));
>                                       C_APPEND_LIST(lst, C_FIX(z));
>                                       C_APPEND_LIST(lst, C_FIX(x));
>                                       C_APPEND_LIST(lst, C_FIX(y));
>                                       return (lst);"))

What I've been doing for the GTK binding is:

static void listof(C_word argc, C_word self, C_word k, C_word param) {
  C_word space[C_SIZEOF_PAIR];
  C_word *a = space;
  C_word p = C_pair(&a, param, C_SCHEME_END_OF_LIST);
  C_kontinue(k, p);
}

and then

  ((##core#primitive "listof") 'hello)   ==>   (hello)

This gets tedious fairly quickly. Plus of course adding in the
required stack-depth checks (if you're allocating a *lot*) is extra
make-work...

> And now what would be the need of the multiple values, or the OUT construct
> that adjusts parameters in the argument list?

Convenience. It'd automate a heck of a lot of repetitive
boilerplate. Chicken could do all the glue required for both multiple
parameters (which it already does) *and* multiple return values....

> Since you can always model IN and OUT easily,

I don't know about easily :-) but it can be done. I think it's clunky
having to write C in continuation-passing-style wherever an OUT or
INOUT parameter occurs.

> the only interesting one is INOUT. INOUT is really just kind of a
> shortcut of a functional application with an accompanying set! so
> that you don't have to explicitly write it(in more practical terms
> though, it is a nice efficiency boost in compiled code). As an
> aside, you have to be careful with an INOUT shortcut since you can't
> pass immediates to the function anymore....

Not necessarily; so long as INOUT values are both in the parameter
list and the result list, you're cool. You could then pass an
immediate, and while it wouldn't get updated in place, the new value'd
be passed back to you so you could update the location holding the
immediate yourself.

;; not a great example. oh well
(define myadd1
        (foreign-lambda void "add1" #:inout int))

(let ((x 42))
        (set! x (add1 x)))

This style works for both immediate and non-immediate...

> C functions without any intervention by the programmer at all. However,
> the compiler has to be designed with this in mind, and I don't know if
> chicken was.

It kind of already does all the checking that's needed. It checks
values on the way in, and unwraps them, and it takes the single
resulting value on the way out, and wraps it appropriately. The only
change would be to generalise it so it knows how to pass in C
locations, too. Pseudo-code for how I imagine the wrappers to look
follows:

(define generate_int (foreign-lambda void "generate_int" #:out integer))
(define add1 (foreign-lambda void "add1" #:inout integer))

static void wrap_generate_int(C_word argc, C_word self, C_word k) {
  C_word space[C_SIZEOF_FLONUM];
  C_word *a = space;
  C_word result = C_SCHEME_UNDEFINED;
  int rv0 = 0;

  generate_int(&rv0);
  result = C_int_to_num(&a, rv0);
  C_kontinue(k, result);
}

static void wrap_add1(C_word argc, C_word self, C_word k, C_word v0) {
  C_word space[C_SIZEOF_FLONUM];
  C_word *a = space;
  C_word result = C_SCHEME_UNDEFINED;
  int rv0 = C_num_to_int(v0);

  add1(&rv0);
  result = C_int_to_num(&a, rv0);
  C_kontinue(k, result);
}



Hmm. That style may not generalise beyond simple types. Hmm. Perhaps
allowing OUT/INOUT for foreign-lambda is a bad idea after all.

Well I'd maybe be happy enough to allow multiple return values from a
foreign-lambda*... this neatly sidesteps the issue of trying to guess
the memory usage patterns of arbitrary C functions. You could have
something like this to get the date out of a struct tm:

(foreign-lambda*
        ((c-string str)
         (int y)
         (int m)
         (int d))
        ((c-pointer tmptr))
        "struct tm *tm = (struct tm *) tmptr;"
        "time_t t = mktime(tm);"
        "result.str = ctime(t);"
        "result.y = tm.tm_year + 1900;"
        "result.m = tm.tm_mon + 1;"
        "result.d = tm.tm_mday;")

...which might expand to

static void f(C_word argc, C_word self, C_word k, C_word v0) {
  struct {
    char const *str;
    int y;
    int m;
    int d;
  } result;
  void *tmptr = C_pointer_address(v0);
  {
    struct tm *tm = (struct tm *) tmptr;
    time_t t = mktime(tm);
    result.str = ctime(t);
    result.y = tm.tm_year + 1900;
    result.m = tm.tm_mon + 1;
    result.d = tm.tm_mday;
    {
      size_t result_str_len = result.str ? strlen(result.str) : 0;
      C_word space[C_SIZEOF_STRING(result_str_len) +
                   C_SIZEOF_LIST(4)];
      C_word *a = space;
      C_word r = C_list(&a, 4, C_string(&a, result_str_len, result.str),
                               C_fix(result.y),
                               C_fix(result.m),
                               C_fix(result.d));
      C_kontinue(k, r);
    }
  }
}


It uses lists rather than multiple values, but then, I don't know how
multiple values are implemented in Chicken. I've just checked -
they're passed as arguments to the continuation, directly, I think! -
so it might look a bit like:

    ... same as before ...
    {
      size_t result_str_len = result.str ? strlen(result.str) : 0;
      C_word space[C_SIZEOF_STRING(result_str_len)];
      C_word *a = space;
      ((C_proc5)(void *)C_block_item(k, 0))(5, k,
                                            C_string(&a,
                                                     result_str_len,
                                                     result.str),
                                            C_fix(result.y),
                                            C_fix(result.m),
                                            C_fix(result.d));
    }
    ...


> Eh, enough rambling. Where's my coffee!

Me too :-) In fact I'm off home to bed.

Tony
-- 
Monkeys high on math -- some of the best comedy on earth
        - Tom Lord, regarding comp.lang.scheme




reply via email to

[Prev in Thread] Current Thread [Next in Thread]