Don't know how to pass a pointer of structure to C function

Q&A's, tips, howto's

Don't know how to pass a pointer of structure to C function

Postby csfreebird » Thu May 15, 2014 3:49 pm

Today, I try to call MongoDB C Driver API in newLISP code.
Below is this C function declaration :
Code: Select all
char **
mongoc_database_get_collection_names (mongoc_database_t *database,
                                      bson_error_t      *error);


I have got database pointer successfully before, but do not know how to pass a pointer as bson_error_t, bson_error_t is a struct in C defined like so:
Code: Select all
typedef struct
{
   uint32_t domain;
   uint32_t code;
   char          message[504];
} bson_error_t;


My newlisp code snippets:
Code: Select all
(import library "mongoc_database_get_collection_names" "void*" "void*" "void*")
(define (get_coll_names s e)
  (mongoc_database_get_collection_names s e))


I called the above function in another file, but got error:
Code: Select all
(Mongo:get_coll_names s e)
ERR: value expected : nil
called from user defined function Mongo:get_coll_names
csfreebird
 
Posts: 107
Joined: Tue Jan 15, 2013 11:54 am
Location: China, Beijing

Re: Don't know how to pass a pointer of structure to C funct

Postby ryuo » Wed May 21, 2014 4:53 pm

Although I am new to newlisp, I have experience with C. I believe the problem you are having is that the function you are trying to call expects a "pointer" to the struct. The struct must be allocated somewhere in memory first, and then you need to feed the address of the instance of that struct to the function. You probably need to retrieve the address via the 'address' function, documented here:

http://www.newlisp.org/downloads/newlis ... ml#address

It only gives examples with strings and integers, but it would be weird if it didn't support other types. This may or may not solve the problem, as it could be something else to do with lisp. But, I just noticed that it looked like you were trying to pass the struct and not a pointer to the struct.

Edit:

I also noticed upon a second look that you don't post code that converts the C struct. A look into the API reveals a struct function:

http://www.newlisp.org/downloads/newlis ... tml#struct

I think you also need to do this in addition to getting the address for the struct.
ryuo
 
Posts: 43
Joined: Wed May 21, 2014 4:40 pm

Re: Don't know how to pass a pointer of structure to C funct

Postby csfreebird » Sat May 24, 2014 5:16 am

Thanks for your advice. I tried a few minutes today. Get some progress with more questions.
In my mongo.lsp file:

Code: Select all
(context 'Mongo)

(set 'files '(
         "/usr/local/lib/libmongoc-1.0.so"))

(set 'library (files (or
            (find true (map file? files))
            (throw-error "cannot find libmongoc library"))))

(import "/usr/local/lib/libbson-1.0.so")
(import "/usr/local/lib/libbson-1.0.so" "bson_new")
(import "/usr/local/lib/libbson-1.0.so" "bson_append_utf8")

(import library "mongoc_client_new" "void*")
(import library "mongoc_client_get_collection")
(import library "mongoc_database_get_collection_names" "void*" "void*" "void*")
(import library "mongoc_collection_find")

(define (get_coll_names s e)
  (mongoc_database_get_collection_names s e))

(define (connect url)
  (mongoc_client_new url))

;; return mongoc_collection_t
(define (get_coll session db_name coll_name)
  (mongoc_client_get_collection session db_name, coll_name))


In my test.lsp file, I wrote:
Code: Select all
(load "mongo.lsp")
(set 'client (Mongo:connect "mongodb://127.0.0.1/"))
(set 'coll (Mongo:get_coll client "kaimei" "user"))

(struct 'bson_error
   "int"
   "int"
   "char*" ;; size is 504
   )

(set 'e (pack bson_error 0 0 ""))
(set 'names (Mongo:get_coll_names coll (address e)))
(println names)


When running test.lsp, I got output
Code: Select all
2014/05/24 13:14:04.0658: [ 3305]:     INFO:      cluster: Client initialized in direct mode.
20444896


That means connection to mongo server is established successufly.
And it seems the get_coll_names returned char**, but I do not know how to parse it into newlisp list.
csfreebird
 
Posts: 107
Joined: Tue Jan 15, 2013 11:54 am
Location: China, Beijing

Re: Don't know how to pass a pointer of structure to C funct

Postby ryuo » Sat May 24, 2014 8:57 pm

Basically you need to dereference the pointer and apply pointer arithmetic. It's doable from newLISP, but this requires behavior which is tied to the size of pointers for the platform the interpreter is running on. For the platforms I know of, you need to know this:

32 bit X86 is 4 bytes and requires get-int function call
64 bit X86 is 8 bytes and requires get-long function call

Ideally, this is something that should be added to the interpreter's builtin functions. The user should at least have a portable way of determining this platform's pointer sizes.

First the C code of the compiled shared library:

Code: Select all
char *xyz[] =
{
  "a",
  "b",
  0
};


Now the code for the newLISP code:

Code: Select all
(import "./test.so" "xyz")

(constant 'PointerSize 8)

(constant 'get-pointer (fn (x) (get-long x)))

(set 'i 0)

(until (= (get-pointer (+ (address xyz) i)) 0)
   (println (get-string (get-pointer (+ (address xyz) i))))
   (set 'i (+ i PointerSize))
)


This is just a sample of how to get the strings. I am assuming that the database library you are using is passing an array of C strings, with the last one being a null pointer. If you are running newLISP on a 32 bit OS, you will need to change the PointerSize to 4 and the get-pointer function to use get-int instead.
ryuo
 
Posts: 43
Joined: Wed May 21, 2014 4:40 pm

Re: Don't know how to pass a pointer of structure to C funct

Postby Lutz » Sun May 25, 2014 6:58 am

Here another way to import char * string arrays using unpack. The format "lu" assumes that pointers are 32bit. The simple FFI syntax is used and the size of the array must be known:
Code: Select all
; directly from the data

> (import "ret_array.so" "data")
data@6E842000
> (map get-string (unpack "lu lu lu" (address data)))
("This is" "an array" "of strings")
>

; via function returning a ptr array

> (import "ret_array.so" "ret_array")
ret_array@6E841240
> (map get-string (unpack "lu lu lu" (ret_array)))
("This is" "an array" "of strings")
>


and this is the C code:
Code: Select all
char * data[] = {"This is", "an array", "of strings"};

char * * ret_array()
{
return(data);
}


For the extended FFI using libffi see the files
http://www.newlisp.org/newlisp-10.6.0/util/ffitest.c and http://www.newlisp.org/newlisp-10.6.0/q ... /qa-libffi for many examples. qa-libffi containes code to compile ffitest.c to a library and works on most platforms - thanks to 'rickyboy' -. Remember that the extended FFI is only available when the word "libffi" appears in the newLISP start banner. The simple FFI used in above example is always available in any flavor of newLISP.
Lutz
 
Posts: 5279
Joined: Thu Sep 26, 2002 4:45 pm
Location: Pasadena, California


Return to newLISP in the real world

Who is online

Users browsing this forum: No registered users and 2 guests

cron