Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

cffi: calls with variable number of arguments determined at runtime

8 views
Skip to first unread message

Lars Rune Nøstdal

unread,
Feb 1, 2007, 10:22:57 AM2/1/07
to
cffi> (with-foreign-pointer-as-string (s 100)
(foreign-funcall "sprintf"
:pointer s
:string "i: %d"
:int 1234
:int))
"i: 1234"


;; ..works ok.. same with..


cffi> (defcfun "sprintf" :int
(str :pointer)
(control :string)
&rest)
sprintf
cffi> (with-foreign-pointer-as-string (s 100)
(sprintf s "i: %d" :int 1234))
"i: 1234"


;; but what if i do not know the types and arguments at compile time?
;; these are stupid examples and none of them work, but:


cffi> (with-foreign-pointer-as-string (s 100)
(let ((type-args* '(:int 1234)))
(sprintf s "i: %d" type-args*)))

cffi> (with-foreign-pointer-as-string (s 100)
(sprintf s format (read-from-string (read-line))))


;; is there some way of doing this using CFFI or possibly something else?


--
Lars Rune Nøstdal
http://nostdal.org/

Pascal Bourguignon

unread,
Feb 1, 2007, 11:50:13 AM2/1/07
to
Lars Rune Nøstdal <larsn...@gmail.com> writes:

> cffi> (with-foreign-pointer-as-string (s 100)
> (foreign-funcall "sprintf"
> :pointer s
> :string "i: %d"
> :int 1234
> :int))
> "i: 1234"
>
>
> ;; ..works ok.. same with..
>
>
> cffi> (defcfun "sprintf" :int
> (str :pointer)
> (control :string)
> &rest)
> sprintf
> cffi> (with-foreign-pointer-as-string (s 100)
> (sprintf s "i: %d" :int 1234))
> "i: 1234"
>
>
> ;; but what if i do not know the types and arguments at compile time?
> ;; these are stupid examples and none of them work, but:
>
>
> cffi> (with-foreign-pointer-as-string (s 100)
> (let ((type-args* '(:int 1234)))
> (sprintf s "i: %d" type-args*)))

Obviously, here you know the type, since you wrote %d, not %s or %f!

Now imagine you have this C function:

void format(const char* ctrlstring,int nargs,void** arguments){
...
}

How can you write it in C, if you don't know the type of the
arguments? The same as you'd do in lisp, and the same printf does,
you parse ctrlstring to collect the types.

--
__Pascal Bourguignon__ http://www.informatimago.com/

NEW GRAND UNIFIED THEORY DISCLAIMER: The manufacturer may
technically be entitled to claim that this product is
ten-dimensional. However, the consumer is reminded that this
confers no legal rights above and beyond those applicable to
three-dimensional objects, since the seven new dimensions are
"rolled up" into such a small "area" that they cannot be
detected.

Lars Rune Nøstdal

unread,
Feb 1, 2007, 12:59:44 PM2/1/07
to

Ok, but there are cases where there is no format string. Or - what I mean
is, the problem is after the "step" you describe. Even if I at runtime
determine:

* types
* values
* ..and of course then also number of these

..I cannot seem to find a way to build the function call (at runtime) and
call it with CFFI. Hm, does this make sense?

Pascal Bourguignon

unread,
Feb 1, 2007, 1:24:40 PM2/1/07
to
Lars Rune Nøstdal <larsn...@gmail.com> writes:

>>> cffi> (with-foreign-pointer-as-string (s 100)


>>> (sprintf s "i: %d" :int 1234))
>>> "i: 1234"
>>>
>>>
>>> ;; but what if i do not know the types and arguments at compile time?
>>> ;; these are stupid examples and none of them work, but:
>>>

> Ok, but there are cases where there is no format string. Or - what I mean
> is, the problem is after the "step" you describe. Even if I at runtime
> determine:
>
> * types
> * values
> * ..and of course then also number of these
>
> ..I cannot seem to find a way to build the function call (at runtime) and
> call it with CFFI. Hm, does this make sense?

(let ((ctrl-string "%d %f %c %s")
(types '(:int :float :char :string))
(values '(42 3.14 #\! "Hi")))
(apply (function sprintf) s ctrl-string
(mapcan (function list) types values)))

--
__Pascal Bourguignon__ http://www.informatimago.com/

"Debugging? Klingons do not debug! Our software does not coddle the
weak."

Lars Rune Nøstdal

unread,
Feb 1, 2007, 1:31:04 PM2/1/07
to
On Thu, 01 Feb 2007 19:24:40 +0100, Pascal Bourguignon wrote:

> Lars Rune Nøstdal <larsn...@gmail.com> writes:
>
>>>> cffi> (with-foreign-pointer-as-string (s 100)
>>>> (sprintf s "i: %d" :int 1234))
>>>> "i: 1234"
>>>>
>>>>
>>>> ;; but what if i do not know the types and arguments at compile time?
>>>> ;; these are stupid examples and none of them work, but:
>>>>
>> Ok, but there are cases where there is no format string. Or - what I mean
>> is, the problem is after the "step" you describe. Even if I at runtime
>> determine:
>>
>> * types
>> * values
>> * ..and of course then also number of these
>>
>> ..I cannot seem to find a way to build the function call (at runtime) and
>> call it with CFFI. Hm, does this make sense?
>
> (let ((ctrl-string "%d %f %c %s")
> (types '(:int :float :char :string))
> (values '(42 3.14 #\! "Hi")))
> (apply (function sprintf) s ctrl-string
> (mapcan (function list) types values)))

sprintf is a macro. Using foreign-funcall doesn't work either as it
expects its arguments to be typenames at expansion time it seems.

cffi> (with-foreign-pointer-as-string (s 100)


(let ((ctrl-string "%d %f %c %s")
(types '(:int :float :char :string))
(values '(42 3.14 #\! "Hi")))
(apply (function sprintf) s ctrl-string

(mapcan (function list) types values))))

Execution of a form compiled with errors.
Form:
#'sprintf
Compile-time error:
The macro name sprintf was found as the argument to FUNCTION.
[Condition of type sb-int:compiled-program-error]

Lars Rune Nøstdal

unread,
Feb 1, 2007, 1:32:21 PM2/1/07
to
I'm starting to think CFFI lacks a way of doing this. Everything seems to
expect that info about types/arguments are available at macro expansion
time.

Pillsy

unread,
Feb 1, 2007, 2:23:23 PM2/1/07
to
On Feb 1, 10:22 am, Lars Rune Nøstdal <larsnost...@gmail.com> wrote:
[...]

> ;; is there some way of doing this using CFFI or possibly something else?

I'm not claiming this is anything but completely gruesome, but this
appears to work:

(defun foreign-apply (name-or-pointer arglist)
(funcall
(compile nil `(lambda ()
(foreign-funcall ,name-or-pointer
,@arglist)))))

so this works:

> (with-foreign-pointer-as-string (s 100)
(foreign-apply "sprintf"
(list :pointer s


:string "i: %d"
:int 1234

:int)))

"i: 1234"

as does this:

> (with-foreign-pointer-as-string (s 100)
(let ((args (list :pointer s


:string "i: %d"
:int 1234

:int)))
(foreign-apply "sprintf"
args)))
"i: 1234"

Disclaimer: Only tested with SBCL 1.0.2. Doesn't really look that much
like plain-old APPLY. Do not taunt FOREIGN-FUNCALL.

Cheers,
Pillsy

Lars Rune Nøstdal

unread,
Feb 1, 2007, 2:34:00 PM2/1/07
to
On Thu, 01 Feb 2007 11:23:23 -0800, Pillsy wrote:

> On Feb 1, 10:22 am, Lars Rune Nøstdal <larsnost...@gmail.com> wrote:
> [...]
>> ;; is there some way of doing this using CFFI or possibly something else?
>
> I'm not claiming this is anything but completely gruesome,

haha .. how hackish .. yes, it does seem to work, and will do for now ..
thanks :)

Lars Rune Nøstdal

unread,
Feb 2, 2007, 11:00:51 AM2/2/07
to
..another thing I sometime miss is being able to do stuff like this:

(defcenum ParamFlags
(:readable (ash 1 0))
(:writable (ash 1 1))
(:construct (ash 1 2))
(:construct-only (ash 1 3))
(:lax-validation (ash 1 4))
(:static-name (ash 1 5))
;; :private deprecated
(:static-nick (ash 1 6))
(:static-blurb (ash 1 7)))

..it only accepts literal integers.

Zach Beane

unread,
Feb 2, 2007, 11:49:11 AM2/2/07
to
Lars Rune Nøstdal <larsn...@gmail.com> writes:

> ..another thing I sometime miss is being able to do stuff like this:
>
> (defcenum ParamFlags
> (:readable (ash 1 0))
> (:writable (ash 1 1))
> (:construct (ash 1 2))
> (:construct-only (ash 1 3))
> (:lax-validation (ash 1 4))
> (:static-name (ash 1 5))
> ;; :private deprecated
> (:static-nick (ash 1 6))
> (:static-blurb (ash 1 7)))
>
> ..it only accepts literal integers.

#. may come in handy there. On the other hand, in that specific case,
I might be more inclined to write:

(defcenum ParamFlags
(:readable #b00000001)
(:writable #b00000010)
(:construct #b00000100)
...)

Zach

Lars Rune Nøstdal

unread,
Feb 2, 2007, 12:49:56 PM2/2/07
to

Thanks for the #. tip. I forgot about that one.

Just found another way:


(defbitfield ParamFlags
(:readable #x00000001)
:writable
:construct
:construct-only
:lax-validation
:static-name
;; :private (deprecated)
:static-nick
:static-blurb)

(foreign-bitfield-symbols 'ParamFlags 237)
=> (:readable :construct :construct-only :static-name :static-nick :static-blurb)


..handy. :)

0 new messages