1. wrapping C functions
- Posted by rkdavis Feb 21, 2013
- 1533 views
It's been many a year since I wrapped any libraries for use with euphoria and i've forgotten anything i knew and i'm trying to wrap the raspberry pi wiringPi library and hit two functions that i can't for the life of me remember how to handle. I've done everything else just those two remain
int wiringPiSPIDataRW (int channel, unsigned char *data, int len) ;
&
extern void serialPrintf (int fd, char *message, ...) ;
please can someone put me out of my misery?
2. Re: wrapping C functions
- Posted by ssallen Feb 21, 2013
- 1618 views
dll_ptr = open_dll("whatever_your_library_is.dll")
wiringPiSPIDataRaw = link_c_func(dll_ptr, "wiringPiSPIDataRW", {C_INT, C_POINTER, C_INT}, C_INT) function euWiringPiSPIDataRaw( integer x, atom yptr, integer z) atom ret ret = c_func(wiringPiSPIDataRaw,{x, yptr, z}) return ret end function
serialPrintf = link_c_proc(dll_ptr, "serialPrintf", {C_INT, C_POINTER, ...}) procedure euSerialPrintf(integer fd, atom message_ptr, ...) c_proc(serialPrintf, {fd, message_ptr, ...}) end procedure
You will need to poke and organize the data for the data and message ptrs according to however the pi expects to see it still.
3. Re: wrapping C functions
- Posted by jimcbrown (admin) Feb 21, 2013
- 1518 views
serialPrintf = link_c_proc(dll_ptr, "serialPrintf", {C_INT, C_POINTER, ...}) procedure euSerialPrintf(integer fd, atom message_ptr, ...) c_proc(serialPrintf, {fd, message_ptr, ...}) end procedure
That's not exactly right. serialPrintf is a vardic function, which isn't directly supported in Euphoria. Though you could do:
serialPrintStr = link_c_proc(dll_ptr, "serialPrintf", {C_INT, C_POINTER, C_POINTER}) procedure euSerialPrintStr(integer fd, sequence message, sequence string) atom message_ptr = allocate_string(message) atom string_ptr = allocate_string(string) c_proc(serialPrintStr, {fd, message_ptr, string_ptr}) free(message_ptr) free(string_ptr) end procedure serialPrintInt = link_c_proc(dll_ptr, "serialPrintf", {C_INT, C_POINTER, C_INT}) procedure euSerialPrintInt(integer fd, sequence message, atom int) atom message_ptr = allocate_string(message) c_proc(serialPrintInt, {fd, message_ptr, int}) free(message_ptr) end procedure
You'd need a new definition for each variation of serialPrintf() that you wanted to handle, though.
4. Re: wrapping C functions
- Posted by ssallen Feb 21, 2013
- 1491 views
hahaha, I just thought he didn't want to type all the parameters out. That would have been extremely confusing. Thanks Jim.
5. Re: wrapping C functions
- Posted by ghaberek (admin) Feb 21, 2013
- 1539 views
That's not exactly right. serialPrintf is a vardic function, which isn't directly supported in Euphoria. Though you could do:
You'd need a new definition for each variation of serialPrintf() that you wanted to handle, though.
Ugh. I hate having to deal with variadic functions in Euphoria. Luckily most C libraries also include statically-defined functions that Euphoria can wrap over for variadic purposes. (e.g. just call the static function for each argument in a list)
However, there is hope! I've recently concocted this method for passing a variable number of arguments to C functions by declaring the function on-the-fly and then caching the result. The code below is from memory but should work just fine. This could be simplified for functions that take n-number of a single type by simply calling repeat( C_TYPE, count ), etc.
include "std/dll.e" include "std/machine.e" include "std/map.e" map serialPrintf_ids = map:new() procedure serialPrintf( integer fd, sequence message, object args ) -- ensure that 'args' is always a sequence of arguments if atom( args ) then args = {args} end if -- declare the base argument types sequence arg_types = {C_INT,C_POINTER} -- append the variadic argument types for i = 1 to length( args ) do if sequence( args[i] ) then -- replace the argument with a pointer to the string... args[i] = allocate_string( args[i], 1 ) -- (1 = clean up automatically) arg_types = append( arg_types, C_POINTER ) elsif integer( args[i] ) then arg_types = append( arg_types, C_INT ) else -- atom (float? could be anything depending on your function) arg_types = append( arg_types, C_FLOAT ) end if end for -- lookup this pattern of argument types in the cache integer id = map:get( serialPrintf_ids, arg_types, -1 ) if id = -1 then -- not found! -- declare this new pattern... id = link_c_proc( dll_ptr, "serialPrintf", arg_types ) -- ...and store it in the cache map:put( serialPrintf_ids, arg_types, id ) end if -- store the message argument in memory atom message_ptr = allocate_string( message, 1 ) -- call the function with our arguments c_proc( id, {fd,message_ptr} & args ) end procedure
-Greg
6. Re: wrapping C functions
- Posted by SDPringle Feb 21, 2013
- 1457 views
That's a little risky, you don't know if the integer might actually need to be a double. You should go through the format string instead and determine the types that way.
7. Re: wrapping C functions
- Posted by petelomax Feb 21, 2013
- 1471 views
That's a little risky
I was thinking that too. Then it dawned on me, there is a far simpler way:
serialPrintf = link_c_proc(dll_ptr, "serialPrintf", {C_INT, C_POINTER}) procedure euSerialPrintf(integer fd, sequence fmt, object args) c_proc(serialPrintf, {fd, allocate_string(sprintf(fmt,args),1)}) end procedure
Pete
8. Re: wrapping C functions
- Posted by ghaberek (admin) Feb 22, 2013
- 1454 views
That's a little risky, you don't know if the integer might actually need to be a double. You should go through the format string instead and determine the types that way.
Agreed. I guess I should have pointed that out. My method is really more applicable to functions that take a variable number of the same type, typically pointers to objects. In any case, I would rely on the programmer to make the correct usage and adapt my method as necessary.
Parsing the format string would certainly work, but would also require much more code for a very small gain (one often knows what one is feeding a printf function). Perhaps we could pass the sequence of types directly...
procedure euSerialPrintf(integer fd, sequence fmt, object args = {}, object types = {}) if atom( args ) then args = {args} end if if atom( types ) then types = {types} end if if length( args ) != length( types ) then crash( "euSerialPrintf: args and types must be the same length! %d != %d\n", {length(args),length(types)} ) end if integer serialPrintf = link_c_proc(dll_ptr, "serialPrintf", {C_INT, C_POINTER} & types) -- ...perform caching here... c_proc(serialPrintf, {fd, allocate_string(fmt,1)} & args) end procedure
I was thinking that too. Then it dawned on me, there is a far simpler way:
serialPrintf = link_c_proc(dll_ptr, "serialPrintf", {C_INT, C_POINTER}) procedure euSerialPrintf(integer fd, sequence fmt, object args) c_proc(serialPrintf, {fd, allocate_string(sprintf(fmt,args),1)}) end procedure
While this method certainly works, it is only applicable in this situations. Other variadic functions may not be so convenient.
-Greg
9. Re: wrapping C functions
- Posted by rkdavis Feb 22, 2013
- 1453 views
That's a little risky
I was thinking that too. Then it dawned on me, there is a far simpler way:
serialPrintf = link_c_proc(dll_ptr, "serialPrintf", {C_INT, C_POINTER}) procedure euSerialPrintf(integer fd, sequence fmt, object args) c_proc(serialPrintf, {fd, allocate_string(sprintf(fmt,args),1)}) end procedure
Pete
thanks, that looks good and will work for this case. i'll "steal" it and make the changes to the includes.